Este documento introduce los conceptos básicos de Java, incluyendo tipos de datos primitivos como enteros, flotantes y caracteres, así como cadenas y matrices. Explica cómo declarar y manipular variables, y realizar conversiones entre tipos de datos. También cubre temas como ámbito de variables y operadores.
4. •
Variables
Una variable no es algo muy diferente de lo que hemos aprendido en matemáticas.
Pensemos en las siguientes operaciones:
– El largo de la parcela es 60 metros
– El ancho de la parcela es 70 metros
– El área es el producto del ancho por el largo: 4200
• ¿Por qué son necesarias las variables?
– Porque necesitamos etiquetas o identificadores para cosas tales como ancho, largo, etc.
– Porque necesitamos almacenar datos asociados a dichos identificadores (60, 70, 4200)
• Un ejemplo en Java:
public static void main(String[] args) {
System.out.println( "Ha entrado en la aplicación");
float largo;
float ancho;
float area;
largo = 60;
ancho = 70;
area = ancho * largo; // ¿ Cómo visualizar el área de la parcela
}
Indice 4
5. Variables: las reglas básicas
• Regla básica: toda variable debe ser declarada antes de ser utilizada
• En el formato de la declaración hay que tener en cuenta:
– Lo básico, especificar:
• El tipo de dato
• El nombre o identificador de la variable
– Lo opcional es:
• Dar valor a la variable por primera vez (inicializar)
• Declarar otras variables en la misma línea (ojo: del mismo tipo)
• Formato:
Tipo nombre [ = valor ] [, nombre [ = valor ] ... ];
• Ejemplos:
int alto, ancho = 0, edad;
char x = ‘s’, b;
• El programador tiene completa libertad a la hora de dar nombre a una variable. Por ejemplo, no
hay obligación de llamar a las variables enteras con nombres como “número”, “entero”, etc.
• Lo mejor a la hora de dar un nombre es dejarse llevar por el sentido común: claridad, es decir,
tratar de ser “significativo” sin alargarse en exceso. Por ejemplo, el nombre “edad” es más
significativo que usar simplemente “e”. La costumbre de dar nombres no significativos es uno de
los vicios que conduce a crear código “solipsista”: sólo lo comprende el programador que lo creó
(siempre que no haya pasado mucho tiempo, en cuyo caso lo normal es que no lo comprenda ni la
persona que lo hizo)
Indice 5
6. Los tipos de datos: enteros y coma
flotantetipos de vehículos existen
• Del mismo modo que existen diferentes
diferentes tipos de variables
• El tamaño en bits, a diferencia de C/C++, es fijo, es decir, no varía en
función de la máquina. Esto facilita la portabilidad
• Enteros:
– byte: 8 bits (-128 a 127)
– short: 16 bits (-32.768 a 32.767)
– int: 32 bits (-2.147.483.648 a 2.147.483.647)
– long: 64 bits (+/- 9x1018)
• En coma flotante, también llamados reales. Se utilizan cuando se precisan
posiciones decimales:
– float: 32 bits (3.4e-038 a 3.4e+038)
– double: 64 bits (1.7e-308 a 1.7e+308)
Indice 6
7. Elegir un tipo de datos
• En principio parece que lo más fácil sería trabajar con un único tipo de
dato. ¿Parece lógico tener diferentes tipos de datos?
• La respuesta más sencilla es responder con una pregunta: ¿parece sensato
tener el mismo tipo de vehículo para transportar 5 personas, transportar
3 toneladas de carga o para llevar a 55 personas?
• Tenemos diferentes tipos de datos con la finalidad de optimizar. Del
mismo modo que no es sensato usar el motor de un autobus para un
turismo, no es sensato emplear 64 bits si queremos contar del 1 al 10
• Por tanto, parece que el primer criterio para elegir el tipo es la
optimización: no malgastar memoria. Pero hay un criterio más
importante, el sentido común, que nos indica que resulta prudente
actuar con holgura. De hecho, en nuestra vida cotidiana no llevamos
nuestros coches siempre llenos y perfectamente optimizados. Sino que
con frecuencia, transportan menos personas que su límite máximo
Indice 7
8. Un ejemplo con double
• Un ejemplo para calcular el área de un círculo
(PI*r2)
public class j01_radio {
public static void main(String[] args) throws IOException
{
double PI = 3.1416;
double radio = 3.2, area;
area = radio * radio * PI; // Calculo el área
System.out.println( "El área es: " + area);
}
}
• Ejercicio: hacer un programa que calcule
volumen de un contenedor a partir de su largo,
ancho y altura (pueden admitir dos decimales).
Indice 8
9. Booleanos
• Es un tipo de dato propio de una lógica binaria: sólo tiene como valores
true o false.
int edad = 0;
boolean mayor_de_edad = false;
edad = 18;
mayor_de_edad = true;
• Es el tipo utilizado para evaluar los condicionales:
int edad = 0;
boolean mayor_de_edad = false;
if (edad >= 18) // Si es verdad que la edad es > ó = que 18
mayor_de_edad = true; // entonces es mayor de edad
if (mayor_de_edad == true) // Si es verdad que es mayor de edad ...
System.out.println( “Puede obtener el carnet B1” );
• El último condicional se puede escribir de manara más cómoda (y más
usual):
if (mayor_de_edad) // Si es verdad que es mayor de edad ...
System.out.println( “Puede obtener el carnet B1” );
Indice 9
10. •
Caracteres (char)16 bits. Se utiliza un
En Java los caracteres se almacenan en variables de
formato estándar e internacional denominado Unicode que admite 65.537
caracteres, de esta forma podemos utilizar desde el latín hasta el arábigo.
Unicode es el formato más utilizado en Internet.
• En el siguiente ejemplo cambiamos el valor de una variable char:
char cuadricula = ‘A’;
System.out.println( “La cuadrícula del mapa es ” + cuadricula );
cuadricula = ‘b’;
System.out.println( “La cuadrícula del mapa es ” + cuadricula );
• El siguiente ejemplo nos muestra como funciona internamente el
ordenador: asocia a cada carácter un número. Puesto que con 16 bits
podemos representar números enteros de 0 a 65.536, entonces podemos
representar 65.537 caracteres. En nuestro ejemplo asociamos a un char el
número 126, que se representa en formato de carácter como ‘~’:
char a = 126;
System.out.println( “El carácter es ” + a );
• Observar que una cadenas de caracteres (String) se delimita por comillas dobles y
los caracteres aislados (char) se delimitan por comillas simples.
Indice 10
11. •
String
String no es un tipo simple (como float, char, etc.), sino una clase que nos ayuda a
manejar de forma sencilla cadenas de caracteres.
• Ejemplos de instancias:
String j = “Hola mundo”;
String k = new String( “Hola mundo” );
• Podemos concatenar cadenas:
String k = "Hola“, String m = "Adios";
String h = "Saludos: " + k + " y " + m;
System.out.println( h );
• Podemos saber su ancho:
String k = “Antonio”;
System.out.println( k.length() );
• Mediante compareTo() podemos ordenar diferentes cadenas (ver más adelante)
• Conversión de númerico a cadena mediante el método static valueOf():
int anio = 1999;
String p = String.valueOf( anio);
System.out.println( p );
• La conversión inversa se hace con métodos static que están en las clases Double,
Float, etc:
radio = Double.parseDouble( cadena ); // Convierto el String en double
• Se puede acceder a un carácter de la cadena:
char car = c.charAt(1);
Indice 11
12. Ámbito de vida
• El ámbito de una variable u objeto es el espacio del programa en el que
esa variable existe. Por ello, se habla de “ámbito de vida”
• De forma general (hay excepciones que veremos más adelante), la vida
de una variable comienza con su declaración y termina en el bloque en
el que fue declarada (los bloques de código se delimitan por llaves: {}).
Por ejemplo, ¿cuál es el ámbito de la variable ‘radio’ y del vector ‘args’?:
public static void main(String[] args)
{
double PI = 3.1416;
double radio = 3;
System.out.println( “El área es” + (PI*radio*radio) );
}
• Más adelante profundizaremos en los diferentes tipos de ámbito
Indice 12
13. •
Conversión deen ocasiones nos conviene
Hemos visto que los datos tienen un tipo. Pero
tipos
convertir un dato de su tipo original a otro
• Tipos de conversión:
– Automática o implícita. El tipo destino es más grande (mayor número de bits) que el
tipo origen (ensanchamiento o promoción):
int Origen = 65;
float Destino = 4.35f;
Destino = Origen; // ¿Cuál es el valor de ‘Destino’?
También hay conversión automática ante tipos compatibles. Por ejemplo, la
asignación de un int a un char que vimos anteriormente
– Explicita. La conversión se produce cuando el tipo destino es más pequeño que el
origen (estrechamiento). Para ello debemos realizar un moldeado (cast):
double Origen = 65.13;
int Destino = 4;
Destino = (int) Origen; // ¿Cuál es el valor de ‘Destino’?
– La conversión por medio de métodos es en realidad una aplicación de los tipos de
conversión anteriores. Para ver ejemplos (transparencia dedicada a la clase String):
• String p = String.valueOf( anio);
• double radio = Double.parseDouble( cadena );
Indice 13
14. Comprender la conversión de tipos
• Supongamos que tenemos en una variable (int) el precio de un producto. Queremos
incrementarlo en un 20%:
int precio = 10000;
precio = precio * 1.2;
• Este código parece correcto, sin embargo el compilador nos informa de un error: “Error
num.: 355 : puede perderse precisión: double, hacía falta int”. ¿Cuál es la razón? Para
entenderlo, conviene comprender como trabaja el ordenador:
– El ordenador trata de realizar el producto (precio*1.2) entre un int y un double (1.2), para lo cual el
ordenador convierte de forma automática el int en un double. Hasta aquí todo va bien, el resultado
de multiplicar dos números double es un double:
precio = precio * 1.2;
(int) (double)
(conversión
automática a
double)
(double)
El problema viene a continuación: no hay conversión automática de un double (el resultado
del producto) a un int (la variable a la izquierda del operador =)
Para que se produzca esta conversión es necesario realizar un moldeado (cast):
precio = (int) (precio * 1.2); // Esto si es correcto
Indice 14
15. •
Matrices (I)
Una matriz es un conjunto ordenado de variables u objetos, con las siguientes características:
– Tienen el mismo tipo
– Tienen el mismo nombre (aunque hay casos poco usuales de matrices anónimas)
– Si tienen el mismo nombre, ¿cómo se diferencia un elemento de otro? La respuesta es por el índice
• Formatos:
– Con new: Tipo nombre[] = new tipo[tamaño]
– Sin new: Tipo nombre[] = {x, y, ...}
• Un ejemplo:
int m[] = new int[3];
m[0] = 5;
m[1] = 9;
m[2] = 2;
System.out.println( m[1] );
Matriz
Valor: 5 9 2
Indice Posición: 0 1 2
15
16. Matrices (II)
• Un ejemplo en el que incremento en un 50% el tercer elemento de la
matriz:
public static void main(String[] args) {
int depositos[];
int num_depositos = 4;
depositos = new int[num_depositos]; // Igual que: int depositos[] = new int[4];
depositos[0] = depositos[1] = 300;
depositos[2] = depositos[3] = 700;
System.out.println( "Litros del segundo deposito:" + depositos[1]);
depositos[2] = (int) (depositos[2] * 1.5); // Incremento de un 50%
System.out.println( "Litros del tercer deposito:" + depositos[2]);
}
• ¿Por qué necesito hacer casting?
Indice 16
17. Matrices (III)
• Es importante destacar una diferencia a la hora de crear una matriz:
– Si trabajamos con tipos simples (int, char, float, double, etc.) tenemos que usar
el operador new una vez, al crear la matriz:
int botes[] = new botes[4];
Botes[0] = 325;
– Si trabajamos con tipos compuestos (todos los demás, es decir, clases de Java
como String, o cualquier otra creada por el programador), entonces hay que usar
dos veces dicho operador: una al crear la matriz y luego tantas veces como
objetos queramos almacenar en ella:
public static void main(String[] args) {
Vehiculo mios[] = new Vehiculo[2]; Vehiculo.java:
mios[0] = new vehiculo(); package xxx;
mios[1] = new vehiculo();
public class Vehiculo {
mios[0].definir_precio_bajo(); private int precio;
mios[1].definir_precio_bajo();
mios[1].mostrar(); void definir_precio_bajo() {
precio = 12000;
} }
void mostrar() {
System.out.println( " Precio:" + precio);
}
}
Indice 17
18. Matrices (IV)
• Para obtener el ancho de una matriz:
Nombre.length. Ejemplo:
String nombres[] = new String[3];
nombres[0] = new String(“Antonio”);
System.out.println( nombres.length ); // Muestra el número 3
• En comparación con C/C++ la copia de
200
matrices completas resulta muy sencilla: 400
int Origen[] = { 200, 400, 600 };
int Destino[] = { 10, 20, 30 };
Destino = Origen;
System.out.println( Destino[0] );
System.out.println( Destino[1] );
Indice 18
• Al intentar acceder a un elemento que está fuera del rango de la matriz se
19. Matrices multidimensionales
• Utilizamos una pareja de corchetes ([]) para
cada dimensión. En el caso de una matriz
bidimensional un ejemplo sería:
int Lista[][] = new int[filas][columnas];
• Java le permite no tener que especificar todas
las dimensiones al principio (con el primer
new). Al menos tiene que especificar la
dimensión más significativa (más a la
izquierda). Además puede hacer que las
dimensiones siguientes (menos significativas)
Indice difieran. El siguiente ejemplo crea una matriz
19
20. Operadores aritméticos
– Suma: +
– Resta: -
– Producto: *
– División: /
– Módulo: % (se puede aplicar a coma flotante y
a int)
– Incremento: ++ (a++ es equivalente a: a=a+1)
– Decremento: -- (a-- es equivalente a: a=a-1)
– Suma y asignación: += (a+=3 es equivalente a: a=a+3)
– Resta y asignación: -= (a-=3 es equivalente a: a=a-3)
Indice 20
21. Operadores relacionales
• El resultado de los operadores relacionales es
un valor boolean (true o false):
– Igual a: == (no confundirlo con el operador de asignación)
– Distinto de: !=
– Mayor que, mayor o igual:>, >=
– Menor que, menor o igual: <, <=
• Ejemplo 1:
int a = 5, b = 2;
boolean c = a >= b;
• Ejemplo 2:
Indice 21
22. ¿Qué ocurre con String?
• A menudo necesitamos comparar cadenas de caracteres, para saber si
una es lexicográficamente igual, mayor o menor que otra. El ejemplo
típico es ordenar una lista de nombres
• Ya sabemos que String no es un tipo simple, sino que es una clase. Para
ayudarnos en la comparación tenemos los métodos compareTo y
compareToIgnoreCase. Podemos saber el orden de las cadenas en
función de lo que devuelvan los métodos:
String j = "Belén", k = "Susana", m = "Belén", n = "Antonio";
if (j.compareTo(m) == 0)
System.out.println( j + " es igual a " + m); Belén es igual a Belén
if (j.compareTo(k) < 0) Belén es menor que Susana
System.out.println( j + " es menor que " + k);
if (j.compareTo(n) > 0) Belén es mayor que Antonio
System.out.println( j + " es mayor que " + n);
• Existe también el método equals( String ), que devuelve true si las
cadenas son iguales. Hay otra versión equalsIgnoreCase(String)
Indice 22
23. Operadores lógicos
• Operan solamente con valores booleanos:
– Conjunción (Y): &&
– Disyunción inclusiva (O): ||
– Negación: !
• Ejemplo:
int edad = 17;
float nota = 6f;
boolean mayoria_edad = edad >= 18;
if (mayoria_edad && nota >= 5)
Indice
System.out.println( "aprobado" );
23
if (!mayoria_edad)
25. Streams (I)
• Cualquier programa realizado en Java que necesite llevar a cabo una operación de
I/O lo hará a través de un stream.
• Un stream, cuya traducción literal es "flujo", es una abstracción de todo aquello que
permite introducir o extraer información. Así, un stream de teclado es un flujo de
entrada para el programa y un stream de pantalla es un flujo de salida del programa
Input:
Teclado
Output:
Pantalla
La vinculación de este stream al dispositivo físico (teclado, pantalla, impresora,
etc.) la hace el sistema de entrada y salida de Java.
Indice 25
26. Streams (II)
• El paquete System define tres clases:
– in (entrada estándar)
– out (salida estándar)
– err (salida de errores estándar)
• La escritura hacia pantalla se hace con System.out
System.out.println(“Hola Mundo”); // Añade nueva línea al final
System.out.print(“Adios”); // NO añade nueva línea al final
• La lectura desde teclado se hace con System.in
Indice 26
27. Stream de entrada por teclado
• Para manejar entradas por teclado tenemos que crear un BufferedReader a partir de System.in:
import java.io.*;
public class inicio {
public static void main(String[] args) throws IOException {
String cadena;
BufferedReader entrada;
entrada = new BufferedReader(new InputStreamReader(System.in)); // Crear objeto de entrada
System.out.println("Escribe tu nombre:");
cadena = entrada.readLine(); //leemos cadena de caracteres mediante readLine()
}
}
• Condiciones previas a la creación de un BufferedReader:
– Tengo que importar las clases que uso (BufferedReader y InputStreamReader), que están en el paquete java.io, por
medio de import.
– Tengo que poner “throws IOException” en la función que usa BufferedReader (ya se explicará más adelante el manejo de
excepciones).
• Una vez creado el objeto de entrada para teclado, mediante BufferedReader, puedo leer la entrada por
teclado mediante una llamada a objeto.readLine() (la entrada termina al pulsar Enter). No olvidar que esta
función devuelve un String, es decir, cualquier cosa que el usuario teclea se almacena como una cadena,
sea “pedro” o “3420”. Java es un lenguaje fuertemente tipado y si queremos que lo tecleado se convierta en
un número habrá que hacer posteriormente una conversión mediante métodos, como veremos en el
siguiente ejemplo.
Indice 27
28. Un ejemplo entrada por teclado y
conversión
public static void main(String[] args) throws IOException {
double PI = 3.1416, radio;
int area_int;
String c;
/* Creo el objeto 'entrada', es un lector de entradas por teclado */
BufferedReader entrada = new BufferedReader( new InputStreamReader(System.in));
System.out.print( "Escriba el radio: " );
c = entrada.readLine(); // Leo un String de la entrada de teclado
radio = Double.parseDouble( c ); // Convierto el String en double radio
System.out.println( "El área es: " + radio * radio * PI);
area_int = (int) (radio * radio * PI); // Calculo el área entera del círculo. Hago casting
System.out.println( "El área (entera) es: " + area_int);
}
• Desde un String existen conversiones a otros tipos: Integer.parseInt( cadena ) o
Float.parseFloat( cadena). La conversión inversa utiliza String.valueOf( numero).
• Para que todo funcione: una vez que se ha dado la orden de ejecución, se debe hacer click
en la ventana de mensajes, de esta forma la ventana de mensajes obtiene el foco de
teclado. A partir de aquí todo lo que se teclea va a dicha ventana.
Indice 28
30. Introducción
• Las sentencias de control nos ayudan a que el flujo de ejecución del programa
deje de tener un carácter lineal.
• Ejemplos de sentencias de control en la vida cotidiana:
– Ejemplo 1:
• Si el cheque es correcto y hay fondos
– Entonces pagar
• Si no:
– No pagar
– Ejemplo 2:
• Mientras la salsa no este densa:
– Calentar y remover
– Ejemplo 3:
• Siga adelante
• Cuando llegue a la plaza:
– Si es hora punta
» Entonces tuerza a la derecha
– Si no:
» Siga todo recto
• Vamos a ver los siguientes tipos de sentencias de control:
– Selección
– Iteración
Indice – Salto 30
31. if / else (I)
• La sentencia básica para realizar bifurcaciones
o ramificaciones. Formato:
if (condición)
sentencia 1;
[else
sentencia 2]
• La condición es una expresión booleana. Si es
true, se hace la sentencia 1; si es false, se
hace la sentencia 2 (si existiese) Precaución:
La tabulación es
• Ejemplo: fundamental para realizar
código legible
int a = 3, b = 9, c;
Indice if (a < b && a != 0) 31
32. if / else (II)
• Si hay más de una sentencia debemos delimitar los bloques por llaves {}. En el
siguiente ejemplo vamos a determinar las ventajas que tiene un cliente en función
de los puntos de su tarjeta de compra:
if (puntos > 1000) {
tipo_cliente = "VIP";
descuento = 0.05f;
}
else {
tipo_cliente = "Normal";
descuento = 0.02f;
}
System.out.println( "Tipo: " + tipo_cliente + "tDescuento: " + descuento*100 + "%");
• Los condicionales pueden ser anidados, es decir, que uno este dentro de otro. En
nuestro ejemplo vamos a añadir otra condición: para los clientes de más de 1000
puntos, si su antigüedad es mayor de 1825 días, les haremos un descuento del
6%:
if (puntos > 1000) {
tipo_cliente = "VIP";
descuento = 0.05f;
if (antiguedad > 1825)
descuento = 0.06f;
Indice 32
}
33. switch
• Formato:
switch ( expresión ) { // expresión: no tiene que ser booleano
case constante1:
secuencia de sentencias
break;
case constante2:
secuencia de sentencias
break;
...
default:
secuencia de sentencias
}
• No olvidar ‘break’. Pero a veces es adecuado omitirlo:
switch ( categoria ) {
case 1:
case 2:
porcen = 0.2; // Tanto para 1 como para 2
break;
case 3:
porcen = 0.15;
break;
default:
Indice 33
porcen = 0.1;
}
34. • Formato:
while
while (condición)
Sentencia
• La sentencia puede ser un bloque delimitado por llaves ({}). El bucle se realiza
mientras la condición sea cierta
• Ejemplo en el que modificamos un vector de números, de tal modo que ponemos
el cuadrado del número que antes estuviese:
double serie[] = {23, 4, 36, 9};
int i = 0;
while ( i < serie.length ) {
System.out.print( "Posición: " + i + "t Anterior: " + serie[i] );
serie[i] = serie[i] * serie[i]; Bucle
System.out.println( "t Cuadrado: " + serie[i] );
i++;
}
Posición: 0 Anterior: 23.0 Cuadrado: 529.0
Posición: 1 Anterior: 4.0 Cuadrado: 16.0
Posición: 2 Anterior: 36.0 Cuadrado: 1296.0
Posición: 3 Anterior: 9.0 Cuadrado: 81.0
Indice 34
35. for (I)
• En la vida cotidiana hay numerosos casos de iteraciones (repeticiones). Por ejemplo, si
alguien tiene que asignar un número a las cinco personas que hay en una habitación haría
algo así:
for (el número es 1; mientras que el número sea < 6; incremento el número)
Digo en voz alta el número, señalando a la persona correspondiente
• De forma semejante, el formato de for es:
for ( inicialización; condición; variación )
Sentencia
• Con un ejemplo puede aclararse. Supongamos que queremos mostrar por pantalla los cuatro
primeros números enteros, empezando por el 3:
– La inicialización sería: empezar con el número 3
– Condición: mientras que el número sea menor que 7 (es aquello que debe cumplirse para poder
volver a repetir la sentencia)
– Variación: incrementar en uno el número
– Sentencia (aquello que se debe iterar o repetir, en nuestro ejemplo 4 veces): mostrar el número por
pantalla
• Ejemplo:
int i;
for ( i = 3; i < 7; i++)
System.out.println( i );
• Nos podemos saltar la inicialización (empieza en el valor previo)
int i = 3;
for ( ; i < 7; i++)
Indice System.out.println( i );
35
36. for (II)
• Vamos a ver paso a paso como actua
1. INICIO: la variable i se inicia a 1
la iteración
2. CONDICIÓN: se comprueba la
condición: ¿ i<3 ? SI
• Ejemplo: 3. SENTENCIA: se ejecuta la sentencia
int i; println
for ( i = 1; i < 3; i++)
System.out.println( i );
4. VARIACIÓN: al terminar el bucle, se
incrementa i. Ahora vale 2
5. CONDICIÓN: se comprueba la
• Al observar la ejecución paso a paso es condición: ¿ i<3 ? SI
importante recordar: 6. SENTENCIA: se ejecuta la sentencia
– Siempre se evalúa la condición ANTES println
de ejecutar la sentencia 7. VARIACIÓN: al terminar el bucle, se
– DESPUÉS de la sentencia siempre se incrementa i. Ahora vale 3
realiza la variación 8. CONDICIÓN: se comprueba la
condición: ¿ i<3 ? NO, fin del
• bucle
Se pueden iterar varias variables, para lo
cual necesitamos comas: for(i=0, k=5; i < 5; 9. IMPORTANTE: al salir del bucle el
i++, k=k+5) valor de i es 3. ¿Cuál sería su valor si
Indice la condición fuese i<=3? 36
37. for (III)
• Igual que los casos anteriores: utilizan {} para acotar conjuntos de sentencias y además son
anidables.
• Ejemplo en el que se calcula la media y el máximo de un vector de números:
public static void main(String[] args) {
float serie[] = {-8, -12, -4, -14};
float media, sumatorio = 0;
int contador;
float maximo = serie[0]; // ¿Funcionaria si asignamos el 0?
/*** Hallamos el máximo y sumamos todos los números ***/
for ( contador = 0; contador < serie.length; contador++ ) {
if (serie[contador] > maximo)
maximo = serie[contador];
sumatorio = sumatorio + serie[contador];
}
/*** Calculamos media. Mostramos la media y el maximo ***/
media = sumatorio / contador;
System.out.println( "La media es " + media + " y el max es " + maximo);
}
• Se pueden clasificar las variables en virtud de su función o uso (hasta ahora las hemos
clasificado en virtud del tipo de dato). Entre estos tipos de usos hay dos que son muy
comunes y aparecen en nuestro ejemplo:
– Variables contador
– Variables sumatorio
Indice 37
38. • Formato:
do-while
do {
Sentencia
} while (condición);
• A diferencia de while, analiza la condición al final del bucle. Por tanto, la
sentencia se realiza al menos una vez
• Ejemplo. ¿Qué hace?:
int i = 1;
do {
System.out.println( i*i*i );
i++;
} while ( i < 3 );
• do-while es muy útil en la gestión de menús. El diseño sería:
do {
Muestro las opciones de menú
Solicito la opción por teclado
switch (opcion) {
En función de la opción elegida realizo una u otra acción
}
} while (la opción no sea “Salir”);
• A continuación puede verse un ejemplo de gestión de menú para una calculadora
Indice 38
39. Calculadora
public static void main(String[] args) throws IOException { /**** En función de la opción: opero o salgo ***/
char opcion; switch (opcion) {
String cadenaTeclado; case 's':
double operando1 = 0, operando2 = 0; case 'S':
System.out.print( operando1 + operando2 );
/* Creo el obj. 'entrada', es un lector de entradas por teclado */
break;
BufferedReader entrada = new BufferedReader( new
case 'r':
InputStreamReader(System.in));
case 'R':
System.out.print( operando1 - operando2 );
do { break;
/********* Mostrar menu y pedir opcion por teclado ****/ case 'p':
System.out.print("rn S - Sumarn R - Restarn P - Producto" + "rn case 'P':
D - Divisiónrn Q - Salirrn Su opción:"); System.out.print( operando1 * operando2 );
cadenaTeclado = entrada.readLine(); // Teclado break;
opcion = cadenaTeclado.charAt( 0 ); // Conv a char case 'd':
case 'D':
/********* Si la opción no es salir, solicito operandos ***/ System.out.print( operando1 / operando2 );
if ( opcion != 'Q' && opcion != 'q') { break;
System.out.print( "Número 1: " ); case 'q':
cadenaTeclado = entrada.readLine(); // Teclado case 'Q':
System.out.print( "Adios" );
operando1 = Double.parseDouble( cadenaTeclado ); // Conv
break;
System.out.print( "Número 2: " );
default:
cadenaTeclado = entrada.readLine(); // Teclado
System.out.print( "Opción no disponible" );
operando2 = Double.parseDouble( cadenaTeclado ); // Conv
} } /////////////////////////////// Fin de switch
} while (opcion != 'Q' && opcion != 'q');
Indice 39
} //////////////////////////////// Fin de función
40. Salto
• Hay sentencias que controlan el flujo de ejecución, de tal forma que
realizan saltos fuera del bloque en el que se encuentran:
– break: salta fuera del bucle o switch
– return: salta fuera del método
• Un ejemplo con break. Busca un nombre en una lista y, si lo encuentra,
sale del bucle:
private static void funcion() {
int i = 0;
String nombre[] = { "Ana", "Belen", "Juan", "Pedro"};
String nombrBuscado = “Belen";
boolean encontrado = false;
while (i < nombre.length) {
if (nombre[i].equals(nombreBuscado)) {
System.out.println(nombre_buscado + " está en la posición " + i);
encontrado = true;
break;
}
i++;
}
if (!encontrado)
System.out.println("No encontrado");
Indice } 40
42. Introducción (I)
• Hasta ahora las clases las hemos usado como soporte al método main. A
partir de aquí vamos a adentrarnos en el manejo de clases
• Una clase puede entenderse como un modelo o patrón: la representación
abstracta de un conjunto
• Un conjunto en la vida cotidiana puede ser definido por sus atributos y/o
por acciones (comportamiento). Por ejemplo:
– El conjunto de los mamíferos con aletas
– El conjunto de los profesionales en extinción del fuego
• Un bombero en concreto sería una instancia del conjunto de los
bomberos
• Cuando definimos el conjunto de los bomberos no hacemos referencia a
ningún bombero en concreto, de la misma manera, cuando definimos una
clase no hacemos referencia ni creamos un objeto o instancia de la clase
Indice 42
43. •
Introducción (II)
Del mismo modo que ocurre con los conjuntos de la vida cotidiana, las clases se definen por
sus atributos y/o métodos (funciones que definen el comportamiento de la clase). Por ahora
vamos a empezar con los atributos.
• Veamos el siguiente ejemplo en el que la clase Inicio hace una instancia de la clase Circulo:
/******** Circulo.java ********/ /********* Inicio.java *****/
package figuras.dominio; package figuras.inicio;
import figuras.dominio.Circulo;
public class Circulo {
public double radio; public class Inicio {
public double PI = 3.1416; public static void main(String[] args) {
} Circulo a; // ERROR
a.radio = 23;
}
}
• Creamos la clase Circulo. Es importante entender que la sentencia “Circulo a;” NO CREA un
objeto, sino que crea una referencia o etiqueta (vacía o nula). Por ello, si queremos acceder
al atributo “radio” para asignarle un valor (23), el compilador nos dará un mensaje de error.
SOLO se crea un objeto si se utiliza new. Lo correcto sería:
Circulo a;
a = new Circulo(); // O bien: Circulo a = new Circulo();
Indice a.radio = 23; 43
44. Introducción (III)
• El error anterior era un error en tiempo de compilación. La mayor parte de
IDEs nos darán un mensaje del estilo “variable no inicializada” antes de
compilar, es decir, el entorno de desarrollo ha detectado que no hay un
objeto, que la etiqueta no hace referencia a un objeto.
• Podemos “engañar” a la mayor parte de los IDEs con el siguiente código, que
se puede compilar sin errores:
Circulo a = null;
a.radio = 23; // Línea número 7
• Decimos “engañar” ya que este código hará que el IDE no nos muestre el
error en tiempo de compilación. Pero el problema es el mismo: no hay objeto
para la etiqueta “a”.
• El error surge en tiempo de ejecución:
java.lang.NullPointerException at figuras.inicio.Inicio.main(Inicio.java:7)
• Este es el error más frecuente en programación Java (y en otros lenguajes
como C/C++) y siempre indica lo mismo: tratamos de acceder a un atributo o
método del objeto, pero ocurre que no hay objeto
Indice 44
45. Introducción (IV)
• Una clase es un patrón o modelo, no crea un
objeto. Se crea un objeto con new
• Cada objeto tiene sus atributos o variables
miembro (hay una excepción a esta regla: los
atributos static).
• En el siguiente ejemplo, el primer círculo tiene
un atributo radio que es diferente al mismo
atributo del segundo círculo. Es más, ocupan
posiciones de memoría diferentes
public static void main(String[] args) {
Indice 45
Circulo a = new Circulo();
46. El primer método
• Vamos a introducir un método en nuestra clase “Circulo”, que
simplemente muestra el área:
public class Inicio { public class Circulo {
public static void main(String[] args) { public double radio;
Circulo a = new Circulo(); public double PI = 3.1416;
Circulo b = new Circulo(); public void mostrarArea() {
a.radio = 23; System.out.println( radio*radio*PI );
b.radio = 35.6; }
a.mostrarArea(); }
b.mostrarArea();
}
}
• Una llamada al método implica un SALTO: el ordenador pasa a ejecutar el
código del método y una vez que este termina se devuelve el control a
main
• Las flechas muestran los saltos que da el control de ejecución del
programa
Indice 46
• El método muestra los atributos de SU OBJETO
47. Formato de los métodos
• El formato de los métodos es:
Tipo_acceso tipo_devuelto Nombre_método( parámetros ) {
Cuerpo del método
}
• El tipo de acceso puede ser:
– Para clases que están en el mismo paquete (por defecto: public):
• public: se puede llamar al método desde fuera de la clase
• protected: se puede llamar al método desde fuera de la clase
• private: no se accede desde fuera de la clase
– Para clases que están en diferentes paquetes (por defecto: protected):
• public: se puede llamar al método desde fuera de la clase
• protected: no se accede desde fuera de la clase
• private: no se accede desde fuera de la clase
• El “tipo devuelto” es el tipo de dato que devuelve el método
Indice 47
48. •
Devolviendo valores
En nuestro ejemplo calculamos el área en println, pero esto no es muy inteligente. Ya que si
necesitamos de nuevo el área, tenemos que volver a calcularla. Lo lógico es realizar el cálculo
EN UN ÚNICO MÉTODO y que este método devuelva el resultado. En el siguiente ejemplo
vamos a crear un método público que devuelve el área:
public class Circulo {
public double radio;
public double PI = 3.1416;
public void mostrarArea() {
System.out.println( getArea() );
}
public double getArea() {
return radio*radio*PI;
}
}
• La flecha muestra como se transfiere el control de ejecución. La sentencia return es una orden
de salto.
• Error de principiante: no hay coherencia entre el tipo que declaramos que vamos a devolver y
el tipo efectivamente devuelto. En nuestro ejemplo hay coherencia:
– Declaramos que vamos a devolver double al escribir “public double obtener_area() ...”
– Efectivamente devolvemos double, el resultado de multiplicar variables de tipo double como radio y PI
Indice 48
49. Introducción a los parámetros
• Veamos el siguiente ejemplo de función que calcula y devuelve el
cuadrado de un número:
double cuadrado() {
return 5*5;
}
• Esto es evidentemente un absurdo, sólo nos sirve si el número es 5.
Resulta más lógico que el método calcule con independencia de cual es el
número base. Para ello, el método debe tener parámetros:
public class Param {
public static void main(String[] args) {
double h = cuadrado(3); // Argumentos
System.out.println( h );
}
/*** Devuelve el cuadrado ***/
public static double cuadrado( double base ) { // Parámetros
return ( base * base );
} El parámetro “base” recibe el argumento 3
}
Indice 49
50. Parámetros: los nombres son lo de
menos
• El principiante poco informado puede pensar que los nombres de los
parámetros dependen de los nombres de los argumentos. Puede creer que si
el argumento se llama “X”, el parámetro debe llamarse “X”:
public static void main(String[] args) {
double largo = 3, ancho = 2;
double h = getArea( largo, ancho);
System.out.println( h );
}
public static double getArea(double largo, double ancho) {
return (largo * ancho);
}
• Este personaje desconoce que los nombres son indiferentes, lo que importa
es que el parámetro, se llame como se llame, recibe el contenido del
argumento (más adelante distinguiremos llamadas por valor de llamadas por
referencia). La aprueba es que el método anterior actúa igual si se escribiese
así:
public static double getArea(double PinPanPun, double segundo) {
return (PinPanPun * segundo);
}
Indice 50
51. Una pequeña excepción a la regla
• anterior
Hemos visto que los nombres son lo de menos en el ejemplo de las variables “ancho” y “largo” (no
hay conflicto porque tienen ámbitos de vida independientes)
• Bien, pero la excepción es: “salvo cuando hay conflicto de nombres con un atributo de la clase”,
aquí hay conflicto con los ámbitos de vida. Supongamos que usamos de parámetros para almacenar
los valores en atributos de un objeto:
public class Camino {
private double largo;
public void setLargo( double largo ) { largo = largo; }
}
• Esto es un error absurdo: almacenamos el valor de la variable parámetro “largo” en ella misma (no
en el atributo). Con el agravante de que ese valor se pierde al terminar el método.
• Lo que queremos es almacenar el valor en el atributo, para lo cual usaremos la palabra reservada
“this”:
public void setLargo( double largo ) { this.largo = largo; }
• “this” es una forma de hacer referencia al objeto, es la forma que tiene el propio objeto de decir
“yo”. Este nombre se usa para hacer referencia a atributos o métodos del objeto. Resumiendo, en
nuestro ejemplo:
– this.largo: variable atributo de la clase
– largo: variable parámetro
• Volveremos más adelante sobre “this” y veremos por qué es incompatible con su uso dentro de
métodos static
Indice 51
52. Parámetros: encapsulando
• En nuestro ejemplo del círculo podemos acceder a los atributos de “Circulo”
directamente desde fuera de la clase:
Circulo a = new Circulo();
a.radio = 3;
• Cambiemos de forma de trabajar. Vamos a hacer que sólo se pueda acceder a los datos
por medio de los métodos. Esto implica que debemos poner métodos que devuelvan
valores (usan return, get) y otros que asignen valores (usan parámetros, set):
public class Circulo {
private double radio;
private double PI = 3.1416;
Reglas importantes:
public double getArea() {
return radio * radio * PI; • Encapsular: desde fuera de la clase sólo
} se accede a los métodos públicos . No se
public void setRadio(double nuevoRadio) { accede directamente a los datos
radio = nuevoRadio;
• Modularizar: separar procesamiento de
} datos de su presentación . Un ejemplo es que
public double getRadio() { los cálculos se hacen en métodos diferentes a la
return radio; presentación de datos (incluso, lo que es aún
} mejor, en clases diferentes). También es una forma
} de hacer software cohesivo
Indice 52
53. •
Encapsular que deben seguirse
En la ingeniería del software se especifican reglas
para ayudarnos a realizar software robusto (no proclive a fallos),
fácilmente entendible y de fácil mantenimiento (modificaciones poco
costosas)
• El principio de “Encapsulamiento” (ocultamiento de información) nos
indica (entre otras cosas) que debemos hacer que los detalles de la clase
estén ocultos para el exterior (también se denomina implementación
encapsulada). Una aplicación de este principio implica que los atributos
serán privados y sólo se puede acceder a ellos desde los métodos de la
clase
• Ventaja: desde el código de la clase controlamos el acceso a los atributos.
En caso de fallo o modificación del código, el programador comprueba los
métodos de la clase y no tiene que revisar todo el código de los otros
archivos
• Los métodos públicos son el intermediario entre los datos y los otros
objetos
Objeto
Los métodos
Método 1 públicos de la
Llamada Datos clase son el
Método 2 interfaz de la clase
Indice 53
54. Un ejemplo de encapsulamiento
• Supongamos que tenemos una clase que representa los productos de un
comercio, en el que hay un precio normal y un precio rebajado:
public class Producto {
private float precio;
private float precioRebajado;
public void setPrecioRebajado( float porcentajeRebaja ) {
precioRebajado = precio * (1-porcentajeRebaja);
}
…
}
• ¿Por qué decimos que es una implementación encapsulada? Por dos razones
ligadas:
– Encapsular datos: sólo se accede al atributo privado “precioRebajado” por medio de
un método público de la clase
– Encapsulamos el cálculo: la forma de calcular el dato queda oculta al exterior de la
clase. Supongamos que debemos cambiar la forma de calcular el precio rebajado (bien
por corregir un error de programación o bien por cambiar la política de rebajas),
entonces sólo tendremos que cambiar el método “setPrecioRebajado()”.
Indice 54
55. Un ejemplo de NO
• encapsulamiento
Tomemos el ejemplo anterior y supongamos que la clase es así:
public class Producto {
public float precio;
public float precioRebajado;
…
• Estoy permitiendo el acceso externo a los datos del producto. Por tanto en
una clase externa se podría escribir algo como esto:
static public void main(…) {
Producto a = new Producto();
a.precio = 25.5;
a.precioRebajado = a.precio *0.80;
…
Producto b = new Producto();
b.precio = 19;
b.precioRebajado = b.precio *0.80;
…
• La irracionalidad suele ser costosa. Si tenemos que cambiar nuestra política
de rebajas tendremos que cambiar todas las líneas de código donde
calculamos el precio rebajado para cada producto, en una implementación
encapsulada sólo tenemos que cambiar el método setPrecioRebajado() de
la clase Producto
• En una implementación encapsulada tendríamos que modificar un bloque
de código, no N bloques de código.
Indice 55
56. Modularizar
• Un diseño modular se basa en la conocida
estrategia de “divide y vencerás”.
Descomponer de forma lógica la aplicación
• Para conseguir la modularidad debemos hacer
que nuestros componentes de software sean
especialistas
• En un ejemplo básico y típico separaremos:
– Inicio de aplicación: paquete y clase donde está
main()
– Clases del dominio de problema: por ejemplo,
Indice paquete figuras.dominio con las clases Circulo, 56
57. •
Un ejemplo de modularización
En el siguiente ejemplo separamos las clases del dominio (en este caso
Circulo.java) de la clase responsable de la presentación:
package figuras.dominio; package figuras.presentacion;
public class Circulo { import figuras.dominio.*;
private double radio;
private double PI = 3.1416; public class VistaFiguras {
public Circulo() { } public static void mostrar( Circulo cir ) {
public Circulo( double nuevoRadio ) { System.out.println( cir.toString() );
setRadio( nuevoRadio ); }
}
public double getArea() { public static void mostrar( Rectangulo rec ) {
return radio * radio * PI; System.out.println( rec.toString() );
} }
public void setRadio( double radio ) { }
this.radio = radio;
}
public double getRadio() { ¿Cómo se haría un main() que use estas
return radio; clases?
}
public String toString() {
return "Radio: " + radio + " Area: " + getArea();
Indice } 57
}
58. •
Constructores
Un constructor es un método al que se llama cuando se crea un objeto con new:
– Sólo son llamados inmediatamente después de la creación del objeto
– Tienen el mismo nombre que la clase y no devuelven valores
– Son públicos
• En nuestro ejemplo vamos a crear un constructor que inicia el radio con 1:
Circulo() { Circulo a = new Circulo();
radio = 1;
} ...
• Pero en muchas ocasiones al crear un objeto nos interesa inicializar atributos (en nuestro
caso el radio). Para ello, los constructores admiten parámetros:
class Circulo {
private double radio;
private double PI = 3.1416;
public Circulo( double nuevoRadio ) { Circulo a = new Circulo(21);
setRadio( nuevoRadio );
} ...
public void setRadio( double radio ) {
this.radio = radio;
}
• Utilizamos el nombre de “constructores” por ser el más extendido, pero no confundirse: el
constructor no construye el objeto; sino que es invocado inmediatamente después de la
construcción
• Si no implementamos un constructor, Java pone uno por defecto (constructor vacío)
Indice 58
59. •
finalize() los objetos creados por new es
En lenguajes como C++ la memoria ocupada por
liberada manualmente, mediante el operador delete . En Java la orientación es
diferente: tiene un procedimiento automático de “recogida de basura”. Este
procedimiento examina las referencias y el ámbito, cuando considera que un
objeto no va a ser utilizado lo elimina, es decir, libera la memoria que ocupa
• Inmediatamente antes de que se libere la memoria se invoca automáticamente al
método finalize(). El programador puede implementarlo cuando desee realizar
una acción antes de la eliminación del objeto:
protected void finalize() {
System.out.println( “Adios” );
}
• Téngase en cuenta que no se puede garantizar el momento exacto en el que el
programa invocará al método:
– El procedimiento de recogida de basura no se ejecuta siempre que se deja de utilizar un
objeto, sino que se ejecuta de manera esporádica
– Además cada interprete de Java tiene su procedimiento de recogida de basura
• Por tanto, no se puede confundir con los destructores de C++. Java no tiene el
concepto de destructor
Indice 59
60. Sobrecarga de métodos
• Java nos permite tener métodos con el mismo
nombre y que se diferencian por sus
package figuras.presentacion;
argumentos: ¿Por qué hace falta hacer import de las
clases del paquete “figuras.domino”? Porque
import figuras.dominio.*; están en un paquete diferente que
“VistaFiguras”
public class VistaFiguras {
public static void mostrar( Circulo cir ) {
System.out.println( cir.toString() );
}
public static void mostrar( Rectangulo rec ) {
System.out.println( rec.toString() );
}
}
Al llamar a un método, Java busca la versión del
método que coincide con los argumentos de la
llamada
Indice 60
61. Sobrecarga de constructores
• Los constructores, como cualquier otro método, pueden ser
sobrecargados:
class Circulo {
private double radio;
private double PI = 3.1416;
/****** Constructores sobrecargados ***/
public Circulo() { }
public Circulo( double nuevoRadio ) {
setRadio( nuevoRadio );
}
public Circulo( Circulo circulo ) {
setRadio( circulo.getRadio() );
}
...
}
• Observe que el último constructor define el radio a partir del radio de otro círculo,
que se ha pasado como argumento
• ¿ Cómo sería la llamada que ejecutase dicho constructor?
Indice 61
62. El proyecto de las figuras. Los
puntos
package figuras.dominio;
public class Punto {
private int x;
private int y;
public Punto(int x, int y) {
setPunto(x, y);
}
public Punto(Punto p) { Aspectos a resaltar:
setPunto(p );
} • Encapsulamiento
public void setPunto(int x, int y) { • Sobrecarga de constructores
this.x = x;
this.y = y; • Constructor “de copia”: Punto(Punto p). Con este
constructor se hace un punto que es una copia (tiene la
} misma posición) que el objeto que se pasa como
public void setPunto(Punto p) { argumento (p)
x = p.getX();
y = p.getY();
}
public int getX() { return x; }
public int getY() { return y; }
public String toString() {
return "(" + x + "," + y + ")";
}
}
Indice 62
63. El proyecto de las figuras. Los
package figuras.dominio;
círculos (I) Aspectos a resaltar:
• Encapsulamiento
public class Circulo { • Un atributo (posicion) es un objeto de otra
private Punto posicion; clase (Punto)
private double radio;
static final public double PI = 3.1416; • PI es static (lo comentaremos más
adelante)
public Circulo() { } • PI es final: es una constante, no puede
public Circulo( double nuevoRadio, Punto nuevaPosicion ) { cambiarse. Esto evita modificaciones
setRadio( nuevoRadio ); accidentales por parte del programador
setPosicion( nuevaPosicion );
}
• Sobrecarga de constructores
public Circulo( double nuevoRadio, int posicionX, int posicionY ) { • Constructor “de copia”: Circulo(Circulo
setRadio( nuevoRadio ); circulo). Con este constructor se hace un
posicion = new Punto( posicionX, posicionY ); círculo que es una copia del objeto que se
} pasa como argumento (circulo)
public Circulo( Circulo circulo ) { • ¿Por qué no hace falta import de la clase
setRadio( circulo.getRadio() ); “Punto”? Porque está en el mismo paquete
setPosicion( circulo.getPosicion());
}
public void setRadio( double radio ) { this.radio = radio; }
public void setPosicion( Punto posicion ) { this.posicion = posicion; }
public Punto getPosicion() { return posicion; }
public double getRadio() { return radio; }
public double getArea() { return radio * radio * PI; }
public String toString() {
return "Radio: " + radio + " Posicion: " + posicion.toString() +
" Area: " + getArea();
}
Indice 63
}
64. El proyecto de las figuras. Los
•
círculos (II)
Interesa distinguir dos constructores:
public Circulo( double nuevoRadio, Punto nuevaPosicion ) {
setRadio( nuevoRadio );
setPosicion( nuevaPosicion );
}
public Circulo( double nuevoRadio, int posicionX, int posicionY ) {
setRadio( nuevoRadio );
posicion = new Punto( posicionX, posicionY );
}
• En el primer caso recibimos un objeto (nuevaPosicion), que “almacenamos” en
el atributo “posicion”. No tiene nada de extraño
• En el segundo caso no se recibe un objeto, sino que recibimos dos variables
(“posicionX” y “posicionY”), sin embargo la clase de los círculos requiere tener
un objeto para el atributo “posicion”. Por ello, se tiene que crear (new) el
objeto de la clase Punto.
Indice 64
65. El proyecto de las figuras. La
visualización
package figuras.presentacion;
Aspectos a resaltar:
import figuras.dominio.*; • Modularidad: separamos la presentación
(“VistaFiguras”) de las clases de dominio
public class VistaFiguras { de problema (puntos, círculos, etc.)
• Sobrecarga de constructores
public static void mostrar( Circulo cir ) {
• ¿Por qué hace falta import de las clases
System.out.println( cir.toString() ); del paquete “figuras.dominio”? Porque
} dichas clases están en un paquete
diferente al de “VistaFiguras”
public static void mostrar( Rectangulo rec ) {
System.out.println( rec.toString() );
}
}
Indice 65
66. El proyecto de las figuras. El inicio
package figuras.inicio; Aspectos a resaltar:
import figuras.dominio.*; • Sobrecarga de constructores
import figuras.presentacion.*; • Usamos constructor de copia
• ¿Por qué hacen falta imports?
class Inicio {
• Si hubiera que hacer la clase
public static void main(String[] args) { Rectangulo, ¿cómo sería?
Circulo primero = new Circulo( 23, 2, 3 );
Circulo copia = new Circulo( primero );
Circulo tercero = new Circulo( 17, new Punto(8,9) );
VistaFiguras vista = new VistaFiguras();
vista.mostrar( primero );
vista.mostrar( copia );
vista.mostrar( tercero );
}
}
Indice 66
67. Llamadas por valor
• Cuando hacemos una llamada a un método, que tiene argumentos que
son tipos simples, se hace una copia del valor y se traslada al parámetro
del método. Para que se entienda:
public static void main(String[] args) {
/******** Llamada por valor (copia del valor) *****/
double d = 11.5;
cuadrado( d ); Copia del valor: 11.5
System.out.println( d );
}
public static void cuadrado( double num ) {
num = num * num;
}
• El programa muestra el número 11.5, es decir, la función no ha
modificado la variable argumento (d).
• Esto significa que la función “cuadrado()” no modifica el valor de la
variable “d”. Modifica una copia de “d”.
Indice 67
68. Llamadas por referencia
• Cuando los argumentos son objetos (no usamos tipos simples) no se pasa una
copia del objeto, sino que se pasa el propio objeto. Los parámetros del método
reciben el objeto (llamada por referencia):
public static void main(String[] args) {
Circulo primero = new Circulo( 23, 2, 3 );
trasladar( primero, new Punto(111,111) ); // Llamada por referencia
vista.mostrar( primero );
}
public static void trasladar( Circulo c, Punto nuevaPosicion ) {
c.setPosicion( nuevaPosicion );
}
• Pasamos a la función trasladar() el circulo “primero” con su nueva posición
• La función modifica el argumento (“primero”)
Indice 68
69. String es una excepción
• Hemos dicho que todas las clases (no los tipos
simples) son pasadas en llamadas por referencia.
• String es una excepción: las llamadas son por
copia.
• En el siguiente ejemplo el método no cambia el
argumento, sigue teniendo el valor “Adios”:
public static void main(String[] args) {
String despedida = "Adios";
cambiarDespedida( despedida ); // String es una excepción: llamada por valor
System.out.println( despedida );
}
Indice public static void cambiarDespedida( String mensaje ) { 69
mensaje = "Hasta luego";
70. Devolución de objetos
Hemos visto que return puede servir para devolver el valor de una variable de tipo simple. También puede
servir para devolver un objeto (no una copia del objeto, sino un objeto). En el siguiente ejemplo los objetos
de la clase “Persona” son capaces de clonarse a si mismos.
public class Persona { public static void main(String[] args) {
private String nombre; Persona ana = new Persona( "Ana", 18 );
private int edad;
Persona clon_de_ana = ana.clonar();
public Persona( String nuevoNombre, int nuevaEdad ) {
// Error, no existe el constructor:
setPersona( nuevoNombre, nuevaEdad );
mostrarSaludo(); // Persona juan = new Persona();
} }
public void setPersona( String nuevoNombre, int nuevaEdad ) {
nombre = nuevoNombre;
edad = nuevaEdad; Hola, acaba de crear una nueva persona: Ana
}
Hola, acaba de crear una nueva persona: Ana
public persona clonar( ) {
Ana se ha clonado a si misma/o
Persona p = new Persona( nombre, edad );
System.out.println( nombre + " se ha clonado a si misma/o");
return p;
}
public void mostrarSaludo() {
System.out.println( "Hola, acaba de crear una nueva persona: " + nombre);
}
}
Indice 70
71. Tipos de ámbito
• Ya vimos que el ámbito de una variable u objeto es el espacio del programa en el
que esa variable existe. Por ello, se habla de “ámbito de vida”
• Los principales tipos de ámbitos son:
– Ámbito de objeto. Los atributos de un objeto (que no son static) viven en el
espacio de vida del objeto y son accesibles por cualquier método del objeto
(siempre que el método no sea static). Por ello, a veces se llaman variables de
objeto o variables de instancia
– Ámbito de método. Variables y objetos declarados en un método. Su ámbito
de vida se ciñe al método en el que fueron declaradas, por ello a veces se
llaman variables de método o función
– Ámbito de clase. Las variables static viven con independencia de que
hayamos hecho instancias de la clase. Podemos acceder a ellas (si son
públicas) usando el nombre de la clase y viven desde que se declara la clase,
por ello se llaman variables de clase
Indice 71
72. Ambitos de objeto y de método
package figuras.dominio;
En el ejemplo de los círculos, public class Circulo {
hay variables de objeto como: private Punto posicion;
private double radio;
– posicion static final public double PI = 3.1416;
– radio
public Circulo() { }
Y variables de clase (PI). public Circulo( double nuevoRadio, Punto nuevaPosicion ) {
setRadio( nuevoRadio );
También hay variables de setPosicion( nuevaPosicion );
método (locales a la función): }
public Circulo( double nuevoRadio, int posicionX, int posicionY ) {
setRadio( nuevoRadio );
posicion = new Punto( posicionX, posicionY ) ;
}
public Circulo( Circulo circulo ) {
setRadio( circulo.getRadio() );
setPosicion( circulo.getPosicion());
}
public void setRadio( double radio ) { this.radio = radio; }
public void setPosicion( Punto posicion ) { this.posicion = posicion; }
public Punto getPosicion() { return posicion; }
public double getRadio() { return radio; }
public double getArea() { return radio * radio * PI; }
public String toString() {
return "Radio: " + radio + " Posicion: " + posicion.toString() +
" Area: " + getArea();
}
Indice 72
}
73. Ámbito de clase. static
• Las variables static viven con independencia de que hayamos hecho instancias de la clase
• Podemos acceder a ellas (si son públicas) usando el nombre de la clase (no hay que hacer instancias)
y viven desde que se declara la clase, por ello se llaman variables de clase. Ejemplo:
class Circulo { public static void main(String[] args) {
private punto posicion; System.out.println( circulo.PI);
private double radio; }
static public double PI = 3.1416;
...
• Todas las instancias de la clase comparten la misma variable static. Cosa que no ocurre con las
variables no static, en estas cada objeto tiene su variable
• En el ejemplo es lógico que el atributo “PI” sea static: el número PI es único y el mismo sea cual sea el
círculo
• Con los métodos static ocurre algo semejante. Son accesibles desde la clase, sin necesidad de hacer
instancias. Ejemplo que convierte una cadena en un double:
String c = new String( “123.72” );
double r;
r = Double.parseDouble( c ); // Double es una clase, no un objeto
• Ahora podemos explicarnos por qué main debe ser static: debe ser accesible por el interprete Java
antes de crear ningún objeto
• Tienen restricciones: no pueden utilizar atributos de objeto (variables no static) ni pueden llamar a
métodos de objeto (métodos no static)
•
Indice Por ello un método static no puede usar la expresión “this”, ya que un método static tiene ámbito de
clase y this por definición tiene ámbito de objeto 73
74. final
• Cuando se declara una variable como final
estamos dando la orden de que no se
modifique. Es como definir una constante
• Debe ser inicializada en la declaración, ya que
cualquier intento de modificarla provoca un
error de compilación
• Utilidad: no permitir que un error de
programación altere el valor de una
constante. Ejemplo:
class circulo {
Indice private punto posicion; 74