You are on page 1of 181

Grado en Ingeniería de Sistemas de Telecomunicación, Sonido e Imagen

Programación 1 y 2

Apuntes de la asignatura
Curso 2014-2015

Escola Politècnica Superior de Gandia

Departament de Sistemes Informàtics i Computació


1

Contenido
1 Introducción 8
1.1 ¿Qué es un ordenador? 8
1.2 Lenguajes de programación 17
1.3 Sistema operativo 21

2 Introducción al lenguaje de programación Java 25


2.1 El primer programa 25
2.2 Fundamentos 29
2.3 Arrays 62
2.4 Clases de biblioteca más usadas 66
2.5 Excepciones 72

3 Crear nuevas clases 74


3.1 Programar 74
3.2 El diseño es lo importante 76
3.3 Implementar un diseño 78
3.4 Otro ejemplo: la clase Punto 82
3.5 Relación entre el diseño de un método, su perfil, y la forma de llamarlo 86
3.6 Reglas de acceso publico/privado 87
2

4 Introducción a la algorítmica 90
4.1 Ejemplos 93
4.2 Algoritmos y coste computacional 115

5 Relaciones entre clases 122


5.1 Tener 124
5.2 Delegación 125
5.3 Herencia 126
5.4 Tenencia y delegación vs. herencia 149
5.5 Clases anónimas 150
5.6 Un ejemplo: Formas 152

6 Excepciones 155
6.1 Escribir nuevas excepciones 157

7 Programación genérica. Plantillas. 159


7.1 Exigiendo requisitos a un tipo de plantilla 162
7.2 Interfaces plantilla 164
7.3 Una Caja<Coche> no es una Caja<Vehiculo> 166
7.4 Algoritmos genéricos 168
7.5 Funciones con cantidad variable de argumentos 178
3

Contenido detallado
1 Introducción 8
1.1 ¿Qué es un ordenador? 8
1.1.1 Hardware del ordenador 9
1.1.2 Ejecución de programas 12
1.2 Lenguajes de programación 17
1.2.1 Ejecución de un programa fuente 20
1.3 Sistema operativo 21
1.3.1 Ficheros 22
1.3.2 Codificación de la información 23
2 Introducción al lenguaje de programación Java 25
2.1 El primer programa 25
2.1.1 Programación en la consola 26
2.1.2 Programación en una plataforma de desarrollo (netbeans) 27
2.2 Fundamentos 29
2.2.1 Palabras reservadas 30
2.2.2 Identificadores 31
2.2.3 Comentarios 34
2.2.4 Clases y objetos. (Tipos y variables) 35
2.2.4.1 Conceptos vinculados a las variables y objetos 37
4

2.2.4.2 Tipos primitivos más usados 38


2.2.4.3 Creación de objetos 40
2.2.4.4 Representación del diseño de una clase 42
2.2.4.5 Diseño desdel punto de vista del programador 44
2.2.5 Interacción con el usuario (funciones de biblioteca) 45
2.2.5.1 En la consola 45
2.2.5.2 En modo gráfico 47
2.2.5.3 Conversión desde/a texto 49
2.2.6 Constantes simbólicas 50
2.2.7 Asignación, expresiones y operadores 51
2.2.7.1 Operadores 52
2.2.8 Estructuras de control del flujo de ejecución() 54
2.2.8.1 if 54
2.2.8.2 else if 56
2.2.8.3 switch 57
2.2.8.4 while 58
2.2.8.5 do while 59
2.2.8.6 for 60
2.3 Arrays 62
2.3.1 Arrays de objetos 65
2.4 Clases de biblioteca más usadas 66
5

2.4.1 java.lang.String 66
2.4.2 Envoltorios de tipos básicos 68
2.4.3 java.lang.Math 69
2.4.4 java.lang.Object 70
2.4.5 java.lang.System 70
2.4.6 java.util.Arrays 70
2.4.7 java.util.ArrayList 71
2.5 Excepciones 72

3 Crear nuevas clases 74


3.1 Programar 74
3.2 El diseño es lo importante 76
3.3 Implementar un diseño 78
3.4 Otro ejemplo: la clase Punto 82
3.5 Relación entre el diseño de un método, su perfil, y la forma de llamarlo 86
3.6 Reglas de acceso publico/privado 87
3.6.1 Los campos son privados 88

4 Introducción a la algorítmica 90
4.1 Ejemplos 93
4.1.1 Máximo común divisor (mcd) 93
4.1.2 sin(x) 96
6

4.1.3 Ordenar 99
4.1.3.1 Algoritmo de selección 100
4.1.3.2 Algoritmo de la burbuja 106
4.1.3.3 Algoritmo de inserción 110
4.2 Algoritmos y coste computacional 115
4.2.1 Algoritmo quicksort 119

5 Relaciones entre clases 122


5.1 Tener 124
5.2 Delegación 125
5.3 Herencia 126
5.3.1 Principales motivos para utilizar herencia 128
5.3.2 Cambios de implementación 132
5.3.3 Contratos de implementación 134
5.3.3.1 Implementaciones parciales 137
5.3.4 Sustituibilidad: herencia correcta 140
5.3.5 Objetos polifacéticos 142
5.3.5.1 Casting 145
5.4 Tenencia y delegación vs. herencia 149
5.5 Clases anónimas 150
5.6 Un ejemplo: Formas 152
7

6 Excepciones 155
6.1 Escribir nuevas excepciones 157

7 Programación genérica. Plantillas. 159


7.1 Exigiendo requisitos a un tipo de plantilla 162
7.2 Interfaces plantilla 164
7.3 Una Caja<Coche> no es una Caja<Vehiculo> 166
7.4 Algoritmos genéricos 168
7.4.1 Algoritmo “para cada” sencillo 169
7.4.2 Visitador 172
7.4.3 Buscador 175
7.5 Funciones con cantidad variable de argumentos 178
Programación G-ISTSI 8

1
Introducción

1.1
¿Qué es un ordenador?
• Procesa información

• Ejemplos
Programación G-ISTSI 9

• Programable

Por eso:
– Hardware: CPU + RAM (procesador + memoria)
– Software: datos+instrucciones.
– Lenguaje de programación: variables + algoritmos

1.1.1
Hardware del ordenador
• CPU (central process unit) RAM (random access memory)
Programación G-ISTSI 10

• Placa base (motherboard)


Programación G-ISTSI 11

• Esquema del ordenador

• Periféricos: disco duro, grabador de cd/dvd, teclado, pantalla, impresora, etc.


la computadora acepte instrucciones y datos almacenados en la memoria muy
rápidamente. LA DIFERENCIA ENTRE MEMORIA Y ALMACENAMIENTO
Un buen ejemplo de esto es cuando el CPU carga unCon programa de una
frecuencia, aplicación,
la gente confunde los términos memoria y almacenamiento,
Programación G-ISTSI
como sería un procesador de palabra o un programa de diseño en la memoria, 12
especialmente cuando describen la cantidad que tienen de cada uno de ellos. El
¿QUÉ ES LA permitiendo así que el DE
LA GUÍA COMPLETA programa
MEMORIAde KINGSTON
esa aplicación trabaje
término
TECHNOLOGY lo más
memoria rápidoa yla cantidad de RAM instalado en la computadora,
se refiere
MEMORIA? eficientemente posible. En términos prácticos, al tener un programa cargado en la
1.1.2 memoria significa que se puede realizar el trabajo másmientras el término
rápido teniendo
computadora.
almacenamiento
Paraque esperar
aclarar
se refiere a la capacidad del disco duro de la
esta confusión común, ayuda el comparar a la
Ejecución de programas
menos tiempo para que la computadora realice las tareas.
computadora con una oficina que tiene un escritorio y un archivero.

• Ejecución de programas (programa = datos + instrucciones)


El proceso comienza cuando El archivero representa el disco
RAM
ingresa un comando desde el duro de la computadora, que
teclado. El CPU interpreta el
MEMORIA Y DESEMPEÑO proporciona almacenamiento para
Indi
vidu comando y ordena a la unidad de todos los archivos e información
ales
Inst disco duro cargar el comando o que necesita en la oficina. Cuando
Se ha probado que el agregar más memoria a la computadora aumenta se desempeño.
rucc
ione
s programa en la memoria. Una vez llega a trabajar toma los archivos
Si no hay suficiente espacio en memoria para toda la información que necesita el
que los datos están cargados en la que necesita ver que están en
a

CPU, la computadora tiene que configurar la opción comomemoria, un archivo


el CPU de memoria
ram

puede accesarlos almacenamiento y los pone en el


rog

virtual. Al hacer esto, el CPU reserva ama espacio en el disco duro para simular memoria escritorio para tener un acceso
El p

rogr mucho más rápido.


el p CPU
RAM adicional. Este proceso
Car
gue se conoce como “intercambio” y hace más lento el fácil mientras trabaja en ellos. El
escritorio es como la memoria de
sistema. En una computadora promedio, toma aproximadamente 200ns (nano
la computadora: mantiene la
segundos) para tener acceso a RAM en comparación con 12,000,000ns para accesar
información.
el disco duro. Para poner esto en perspectiva, ¡esto es equivalente a una tarea que
Unidad de disco duro
normalmente toma 3 1/2 minutos, podría tomar hasta 4 1/2 meses para completarla!
• Comparación tiempo
Este proceso de acceso
de colocar memoria
cosas en el RAM
CPU necesita – disco
un Considere
lugar donde duro
pueda obtenerlos
la metáfora del escritorio y el archivero por un momento. Imagínese como
más rápidamente y es similar a la colocación de diversos sería si archivos
cada vez electrónicos
que quisiera very un documento o carpeta tuviera que sacarlo del
12 Comparación de tiempo de
documentos3que
9 3
/ está utilizando en la computadora enarchivero.
12 una solaEsto
carpeta
haríao mucho
directorio.
acceso entre RAM y el
más lento el trabajo, sin mencionar que lo volvería loco.
Minutes
RAM Al hacer esto, mantiene todos los archivos que necesitaSin
6 a laelmano
espacio
discoadecuado
y evita en elenescritorio, nuestra metáfora para la memoria, usted
duro.buscarlos
JANUARY

JANUARY

distintos lugares cada vez que los necesita. 4 / puede distribuir los documentos que va usar y obtener información de éstos
JANUARY

12 JANUARY

Months
JANUARY

inmediatamente, con frecuencia sólo con un vistazo.


Unidad
8 de disco duro Tiempo de espera (tiempo de acceso convertido a min.)
Aquí hay otra diferencia importante entre memoria y almacenamiento: la información
almacenada en el disco duro permanece intacta, incluso cuando se apaga la
computadora. Sin embargo, si se apaga la computadora, se pierden los datos que se
ACTUALIZACIÓN DE MEMORIA EN UNA PC: LA VIDA ES BUENA
mantengan en la memoria. En la metáfora del espacio en el escritorio, es como si se tirara
cualquier
Si alguna vez ha agregado memoria a la PC, tal vez archivouna
haya notado que mejora
se deje sobre
de el escritorio cuando termina su jornada de trabajo.
rendimiento inmediata. Con una actualización de memoria, las aplicaciones
9
responden más rápido, las páginas Web se cargan más rápido y puede tener más
Programación G-ISTSI 13

• Esquema memoria RAM (conjunto de celdas numeradas)

1 celda = 8 bits = 1 byte (1 bit = 0 o 1)


1 kilobyte = tradicionalmente 1024 bytes (210 bytes) pero según el estandar del Sistema
Internacional de Unidades (SI), sería 1000 bytes (103 bytes)
1 megabyte = tradicionalmente 220 bytes (210 Kbytes), 106 bytes según el SI
1 gigabyte = tradicionalmente 230 bytes (210 Mbytes), 109 bytes según el SI
1 terabyte = tradicionalmente 240 bytes (210 Gbytes), 1012 bytes según el SI
Programación G-ISTSI 14

• Fragmento de programa ejecutable 1 en código máquina (instruciones y datos):

direccion en memoria: código (hexadecimal)

000bdc0: 8b74 2404 8b7c 2408 c9e9 f637 0000 8b1c


000bdd0: 248b 7424 048b 7c24 08c9 c355 89e5 83ec
000bde0: 0889 1c24 8974 2404 8b5d 088b 750c 8b4e
000bdf0: 408b 5340 8b42 2c39 412c 7f25 7c2a 8b42
000be00: 3039 4130 7f1b 7c20 8d43 4489 450c 8d46
000be10: 4489 4508 8b1c 248b 7424 04c9 e9a3 3700
000be20: 00b8 ffff ffff eb05 b801 0000 008b 1c24
000be30: 8b74 2404 c9c3 5589 e583 ec08 891c 2489
000be40: 7424 048b 5d08 8b75 0c8b 4e40 8b53 408b
000be50: 421c 3941 1c7f 257c 2a8b 4220 3941 207f
000be60: 1b7c 208d 4344 8945 0c8d 4644 8945 088b
000be70: 1c24 8b74 2404 c9e9 4837 0000 b8ff ffff
000be80: ffeb 05b8 0100 0000 8b1c 248b 7424 04c9

1 /bin/ls
Programación G-ISTSI 15

• Instrucciones máquina que ejecuta el procesador


– aritmético-lógicas (sumar, restar, multiplicar, dividir, desplazar bits, comparaciones (menor,
igual, mayor), AND, OR, NOT, ...)
– movimiento de información (memoria, registros, periféricos), modificación de registros y
celdas de memoria
– control del programa (cambio del registro contador de programa, y del registro del puntero
de pila, saltos, repeticiones, ...)

Fragmento de programa 2 (dirección – codigo – lenguaje ensamblador)

0000BDAA B80100 mov ax,0x1


0000BDAF EB1D jmp short 0xbdce
0000BDB4 89450C mov [di+0xc],ax
0000BDB7 8D4744 lea ax,[bx+0x44]
0000BDBA 894508 mov [di+0x8],ax
0000BDBD 8B1C mov bx,[si]
0000BDBF 248B and al,0x8b
0000BDC1 7424 jz 0xbde7
0000BDC3 048B add al,0x8b
0000BDC5 7C24 jl 0xbdeb

2 ndisasm /bin/ls
Programación G-ISTSI 16

0000BDC7 08C9 or cl,cl


0000BDC9 E9F637 jmp 0xf5c2
0000BDCE 8B1C mov bx,[si]

• Mecánica de ejecución: 1. lectura de instrucción. 2. análisis de instrucción. 3. ejecución de


instrucción.
Programación G-ISTSI 17

1.2
Lenguajes de programación
Problemas para programar a nivel de código máquina/lenguaje ensamblador:

• instrucciones muy elementales: necesidad de escribir mucho código, incluso para tareas simples
• instrucciones dependientes del procesador: no portable
Programación G-ISTSI 18
Programación G-ISTSI 19

Nivel de los lenguajes de programación:


Programación G-ISTSI 20

1.2.1
Ejecución de un programa fuente
Programación G-ISTSI 21

1.3
Sistema operativo
• Problema: dificultad de gestión del ordenador (hardware diferente, tareas complejas de bajo
nivel, ...)

• solución: sistema operativo


– proporciona facilidades de uso a los programas frente a la heterogeneidad del hardware
– gestiona la ejecución de programas (procesador y memoria)
– gestiona los dispositivos (discos, teclado, pantalla, impresora, red, ...)
– lleva asociado uno o más programas de atención al usuario
Programación G-ISTSI 22

1.3.1
Ficheros
• Unidad de información persistente vinculada a un nombre y organizada en un sistema jerárquico
(carpetas o directorios)
• Ilusión del sistema operativo
Programación G-ISTSI 23

1.3.2
Codificación de la información
• Problema: ¿cómo guardar informacón “humana” (texto, imagen, música, video, ...) en un
ordenador (números)?
• Código: tabla unívoca símbolo ↔ número
– texto: ascii (.txt), ms-word (.doc), openoffice (.odg), ...
– documentos: pdf, tiff, ...
– imagen: bmp, jpeg, gif, png, ...
– música: wav, mp3, ...
– vídeo: mpeg, avi, mov, wmv, ...
• Los programas leen los números y muestran los símbolos o toman los símbolos y guardan los
números
Programación G-ISTSI 24

• Tabla ASCII (american standard code for information interchange)

486f 6c61 2061 2074 6f64 6f73 200a


Programación G-ISTSI 25

2
Introducción al lenguaje de programación Java

2.1
El primer programa
public class HolaMundo {

public static void main(String[] args) {

System.out.println ("hola mundo");

javax.swing.JOptionPane.showMessageDialog(null, "hola a todos");

} // ()

} // class
Programación G-ISTSI 26

2.1.1
Programación en la consola

Editor de texto

Consola
Programación G-ISTSI 27

2.1.2
Programación en una plataforma de desarrollo (netbeans)
• Crear un proyecto (sólo la primera vez)

Archivo → Nuevo Proyecto o


Programación G-ISTSI 28

• Editar, compilar y ejecutar


Programación G-ISTSI 29

2.2
Fundamentos
Programación G-ISTSI 30

2.2.1
Palabras reservadas

abstract continue for new this


assert default package private throw
boolean do if protected this
break double implements public throws
byte else import return transient
case enum instanceof short try
catch extends int static void
char final interface strictfp volatile
class finally long switch while
float native super

siempre en minúscula
Programación G-ISTSI 31

2.2.2
Identificadores
• Nombres elegidos por el programador para identificar a
– objetos (variables)
– constantes simbólicas
– métodos (funciones)
– clases (tipos)
• Reglas
– cualquier combinación sin espacios en blanco de letras (a-z, A-Z) y dígitos (0-9)
– que no empiece por dígito
– ni sea palabra reservada
Programación G-ISTSI 32

• Convenciones
Las letras de los identificadores son en minúscula, excepto:
– la primera letra (depende del tipo de identificador, ver a continuación)
– si un identificador está compuesto por varias palabras unidas, la primera letra de cada
palabra (desde la segunda) es mayúscula
– las constantes: todas las letras en mayúscula
En concreto

paquete 3 en minúscula, separado por .


java.io
java.net

clase primera letra mayúscula


class Punto
class PuntoEnTresDimensiones

interfaz primera letra mayúscula


interface Escuchador

3 prefijo
Programación G-ISTSI 33

método preferiblemente infinitivo o imperativo, primera letra en minúscula


unir()
calcularDistancia()

método accesor get...


getLongitud()

método mutador set...


setLongitud()

variables primera letra en minúscula (no será un verbo)


int longitud;
int numeroDePersonasEnEstaClase;

constantes en mayúscula (palabras separadas por _)


public static final double PI = 3.141592654;
public static final int NUMERO_MAX = 1000;
Programación G-ISTSI 34

2.2.3
Comentarios
// esto es un comentario de una linea

/* esto es un comentario
de
varias lineas */

Conviene comentar:

• La cabecera de los ficheros fuente: nombre, fecha, contenido, autor.


• las clases: para qué sirven
• los métodos: qué hacen, qué reciben y qué devuelven.
• las declaraciones de variables y constantes.
• las sentencias de código, intercalando como comentario el algoritmo que implementan
Programación G-ISTSI 35

2.2.4
Clases y objetos. (Tipos y variables)
clase conjunto de objetos con similares características (propiedades y com-
portamientos)

objeto individuo con identidad propia única pero con características similares
a otros individuos de su misma clase.

clase ≡ tipo ≡ familia


objeto ≡ variable ≡ individuo
Ejemplo: clase Coche, objetos 1234ABC, 4235CDC
Programación G-ISTSI 36
Programación G-ISTSI 37

2.2.4.1
Conceptos vinculados a las variables y objetos
Programación G-ISTSI 38

2.2.4.2
Tipos primitivos más usados
char guarda 1 símbolo de la tabla ascii 4.
constantes 5: ’a’ ’b’ ’8’ ’(’ ...
char letra;
letra = ’J’;

boolean guarda 1 valor lógico: true o false


boolean aprendoMucho;
aprendoMucho = true;

4 Unicode en realidad
Programación G-ISTSI 39

int guarda 1 entero


constantes: 1234, -423 ...
int a = 2;
a = (5*a) + 8;

double guarda 1 número real


constantes: 8.0 3.14, -0.438 ...
double x = 3.141592654;
x = Math.sqrt(x);
Programación G-ISTSI 40

2.2.4.3
Creación de objetos
Programación G-ISTSI 41

Es decir,

• un objeto sólo se crea con new() 6


• si una referencia se asigna a otra (otro = nombre) sigue habiendo un objeto (no hay copia):
las dos referencias apuntan al mismo objeto.
• si se utliza una referencia que no apunta a nada: NullPointerException
• se puede hacer que una referencia no apunte a nada: otro = null;
• cuando un objeto no es apuntado por ninguna referencia ya no es utilizable: se “recicla”.

6 Puede que que al llamar a una función, ésta lo cree –con new()– y lo devuelva.
Programación G-ISTSI 42

2.2.4.4
Representación del diseño de una clase

• público: accesible desde código escrito dentro o fuera de la clase


• privado: accesible sólo desde dentro de la clase
• campos: variables privadas globales, comunes a todos los métodos de la clase.
Programación G-ISTSI 43

• método: acción vinculada a un objeto o a una clase. Puede tener datos de entrada, de salida
y puede consultar o modificar los campos.
• constructor: método especial que sólo se ejecuta una vez (por objeto): al crearlo (con new()).
Se encarga de inicializar los campos. Toda clase debe tener uno al menos.
• método no estático: su acción depende del estado de la clase
Se invoca sobre una referencia a objeto:
String nombre = new String("Jordi");
int l = nombre.length();

• método estático: su acción no depende del estado de la clase. (Sólo puede acceder a otros
elementos estáticos).
Se invoca sobre el nombre de una clase:
double z = Math.sin(2.35);

• Las clases pueden relacionarse mediante herencia. Si Coche deriva/hereda de Vehiculo, un


objeto de tipo Coche es también de tipo Vehiculo y tiene los métodos de Coche y de
Vehiculo.
Programación G-ISTSI 44

2.2.4.5
Diseño desdel punto de vista del programador
Programación G-ISTSI 45

2.2.5
Interacción con el usuario (funciones de biblioteca)

2.2.5.1
En la consola
Leer y escribir
public class MiPrograma {
public static void main (String[] args ) throws java.io.IOException {

java.io.BufferedReader entrada =
new java.io.BufferedReader ( new java.io.InputStreamReader (System.in));
String linea;

System.out.println (" escribe algo ");


linea = entrada.readLine();

System.out.println (" has escrito " + linea);


} // main ()
} // class
Programación G-ISTSI 46

Otra forma de leer en la consola 7:


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

java.util.Scanner sc = new java.util.Scanner (System.in);


String s = sc.nextLine ();
double f = sc.nextDouble ();
int i = sc.nextInt ();

System.out.println ("i vale ->" + i + "<-");


System.out.println ("f vale ->" + f + "<-");
System.out.println ("s vale ->" + s + "<-");

} // ()
} // class

7 Ojo con leer un string después de un número: queda un salto de línea. Ojo con la coma decimal de los números reales :
puede ser 4.5 o 4,5 dependiendo del ordenador.
Programación G-ISTSI 47

2.2.5.2
En modo gráfico
• Leer y escribir
import javax.swing.JOptionPane;

public class MiPrograma


public static void main (String[] args ) {

String linea = JOptionPane.showInputDialog("Escribe algo");


JOptionPane.showMessageDialog(null, linea);

} // main ()c class MiPrograma {


} // class
Programación G-ISTSI 48

• Petición de confirmación
import javax.swing.JOptionPane;

public class MiPrograma


public static void main (String[] args ) {

int resp = JOptionPane.showConfirmDialog(null, "¿descansamos?");


System.out.println (resp);

// resp puede valer


// JOptionPane.YES_OPTION
// JOptionPane.NO_OPTION
// JOptionPane.CANCEL_OPTION
} // main ()
} // class
Programación G-ISTSI 49

2.2.5.3
Conversión desde/a texto
(Funciones de biblioteca)

• String → entero
String s = new String("1234");
int i = Integer.parseInt(s);

Si falla la conversión: NumberFormatException.


• String → real
String s = new String("43.21");
double x = Double.parseDouble(s);

• número (entero o real) → String


double x = 1526.53;
String s = x.valueOf(x);

También sirve, como expresión,


"" + x
Programación G-ISTSI 50

2.2.6
Constantes simbólicas
Identificador con un valor fijo asociado.
class MiClase {
public static final double PI = 3.141592654;

public static final int NUMERO_MAX = 1000;


}

// uso
MiClase.PI
MiClase.NUMERO_MAX
Programación G-ISTSI 51

2.2.7
Asignación, expresiones y operadores
Asignación
variable = expresión_del_mismo_tipo;
Algorítmicamente:
var ← expresion

Expresión

• constante: (1234 ’J’ 0.1304 "jordi" false)


• constante simbólica (Math.PI Math.E)
• variable
• cálculo con operadores
Programación G-ISTSI 52

2.2.7.1
Operadores
aritméticos + - * / %
entero op entero → entero, real op real → real
int a = 7 / 2;
int b = 7 % 2;
double x = 7.0 / 2.0;

de relación < <= > >= == !=


tipo op tipo → boolean
boolean v = (a >= 5);
if (a == 8) {
...
}
Programación G-ISTSI 53

lógicos && || ! (and or not)


boolean op boolean → boolean
boolean seguir = true;
if (a>=0 && seguir) {
seguir = !seguir;
...
}

incremento/decremento ++ – –
int a=0;
a++;

ternario ( ? : )
int z;
z = ( a<0 ? 0 : a);
Programación G-ISTSI 54

2.2.8
Estructuras de control del flujo de ejecución()
Bloque grupo de sentencias delimitadas por { }

2.2.8.1
if
if ( condicion ) {
bloque // cuando es verdadera
} else {
bloque // cuando es falsa
}

• La condición es una expresión boolean


• Puede omitirse la parte del else
Programación G-ISTSI 55

Algorítmicamente:
si condición

... // cuando es veradera

si no

... // cuando es falsa


Programación G-ISTSI 56

2.2.8.2
else if
if ( condicion_1 ) {
...
} else if ( condicion_2 ) {
...
} else if ( condicion_3 ) {
...
} else {
... // si no se ha cumplido ninguna
}
Programación G-ISTSI 57

2.2.8.3
switch
switch ( expresion_entera_o_char ) {
case valor_1:
...
break;
case valor_2:
...
break;
case valor_3:
...
break;
default:
...
break;
}
Programación G-ISTSI 58

2.2.8.4
while
while ( condicion ) {
...
}

Algorítmicamente:

mientras condición
...
Programación G-ISTSI 59

2.2.8.5
do while
do {
...
} while ( condicion );
Programación G-ISTSI 60

2.2.8.6
for
for (inicializacion ; condicion ; incremento) {
...
}

Equivale a:
inicializacion;
while (condicion) {
...
incremento;
}
Programación G-ISTSI 61

Frecuentemente:
for (i=0; i<=n-1 ; i++) {
...
}

Algorítmicamente:

i
0 −−−−−−−−−−−−−→ n − 1
...
Programación G-ISTSI 62

2.3
Arrays
Array objeto que guarda una lista de valores indexados (0 a n − 1)

1. Declaración de la referencia
double[] numeros;

2. Creación del array


numeros = new double[30];

Una vez creado, el tamaño es fijo.


Programación G-ISTSI 63

3. Uso
– tamaño
numeros.length

– acceso a casillas
numeros[3] = 2.4;

si se accede a una casilla inexistente: ArrayIndexOutOfBoundsException


– recorrido
for (int i=0; i<=numeros.length-1; i++) {
// hacer algo con numeros[i]
}

– recorrido compacto
for (double x : numeros) {
// hacer algo con x
}

– Declaración, creación e inicialización en un paso.


double[] numeros = {23.4, 82.62, -23.51, 56.8002};
Programación G-ISTSI 64

Recordemos: la asignación de referencias, no copia

Para copiar habría que escribir un bucle que copie o utilizar métodos de biblioteca (java.util.Arrays.copyOf()
Programación G-ISTSI 65

2.3.1
Arrays de objetos
Array de objetos ≡ array de referencias.
1. Declaración de la referencia y creación del 2. Uso
array palabras[2] = new String("jordi");
String[] palabras = new String[7];
Programación G-ISTSI 66

2.4
Clases de biblioteca más usadas

2.4.1
java.lang.String
Programación G-ISTSI 67

String nombre = new String("Jordi");


String apellido = new String("Bataller");
String todo;

int l;
l = nombre.length();

todo = nombre.concat(apellido); // tambien: todo = nombre + apellido;


System.out.println (todo);

• Los objetos String son inmutables.


• Más en paquete java.lang
Programación G-ISTSI 68

2.4.2
Envoltorios de tipos básicos
• Son objetos (inmutables) que guardan un valor de tipo primitivo. Agrupan métodos de utilidad.
– char → Character
– boolean → Boolean
– int → Integer
– double → Double
Ver paquete java.lang
Programación G-ISTSI 69

2.4.3
java.lang.Math
Funciones 8 y constantes mátematicas. Ver paquete java.lang
x = Math.abs( y );
x = Math.sin( y );
x = Math.cos( y );
x = Math.tan( y );
y = Math.asin( x );
y = Math.acos( x );
y = Math.atan( x );
x = Math.ceil( y );
x = Math.floor( y );
x = Math.exp( y );
x = Math.log( y );
a = Math.min( b, c);
a = Math.max( b, c);
a = Math.pow( b, c);
x = Math.random();

8 Métodos estáticos
Programación G-ISTSI 70

2.4.4
java.lang.Object
Todas las clases de Java derivan de esta: son también de tipo Object y heredan sus métodos.
Ver paquete java.lang

2.4.5
java.lang.System
Clase no instanciable con métodos de utilidad.

2.4.6
java.util.Arrays
Clase no instanciable con métodos de utilidad para la manipulación de arrays: búsquedas, com-
paciones, copias, ordenar, ...
Ver paquete java.util
Programación G-ISTSI 71

2.4.7
java.util.ArrayList
Clase que implementa un array redimensionable de objetos.
ArrayList<String> palabras = new ArrayList<String>();

palabras.add ( new String("hola") ); // an~ade


palabras.add ( new String("jordi") ); // an~ade
palabras.add ( new String("bataller") ); // an~ade
palabras.add ( new String("adios") ); // an~ade

int i = palabras.size(); // taman~o


String s = palabras.get ( 2 ); // accede

palabras.set ( 1, new String("Jordi") ); // cambia


palabras.remove ( 3 ); // elimina
palabras.clear (); // vacia

Más en paquete java.util


Programación G-ISTSI 72

2.5
Excepciones
• Excepción: situación anómala durante la ejecución de un progama, en muchos casos debida a
una condición del entorno (y no a un fallo de programación)
• Puede ser
– No corregible: el programa termina
– Corregible, el programa termina si no se ha previsto la corrección.

En Java

• Una excepción es un objeto derivado de la clase java.lang.Excepcion, como por ejemplo


java.io.IOExcepction (error en entrada/salida).
• Muchos métodos de biblioteca avisan que al utilizarlos pueden disparar una excepción en caso
de que fallen. Por ejemplo, en java.lang.Integer vemos
public static int parseInt(String s) throws NumberFormatException
Programación G-ISTSI 73

• Cuando utilizamos uno de estos métodos tenemos 2 alternativas


1. Capturar la excepción
try {
// codigo habitual, con llamadas a metodos que pueden fallar
...
...
} catch (LaQueSea1Excepction e) {
// hacer algo si se dispara LaQueSea1Exception
...
} catch (LaQueSea2Excepction e) {
// hacer algo si se dispara LaQueSea2Exception
...
}

Si una llamada del código habitual falla, la ejecución salta inmediatamente al catch con
el tipo de excepción disparada por el fallo.
2. Pasar la excepción a quien nos llame: añadiendo throws LaQueSeaException al final del
nombre del método y antes de {
) ... {

En este caso, si hacemos una llamada y ésta falla, nuestro método también falla, rediri-
giendo la excepción a quién nos llamó.
Programación G-ISTSI 74

3
Crear nuevas clases

3.1
Programar
• Un programa es la solución a un problema
• Un problema se enuncia mediante una serie de requerimientos que definen las distintas inter-
acciones que pueden darse entre el usuario y el programa.
Para cada interacción se detalla los datos de entrada y de salida (requerimientos funcionales)
y comportamiento y aspecto (requerimientos no funcionales).
Esto puede modelarse con la ayuda de diagramas de casos de uso de UML (Unified Modelling
Language).
• El siguiente paso es realizar el diseño de la arquitectura del programa. Se trata de decidir
– qué piezas forman el programa: en OO son las clases
– qué características tiene cada clase (qué metodos públicos tiene)
– cómo están relacionadas las clases entre sí (las principales relaciones son hereda de, tiene,
usa).
– cómo interactúan los objetos del programa en cada caso de uso (a qué otros métodos
llama un método dado).
Programación G-ISTSI 75

Para diseñar las clases y sus relaciones se puede utilizar un diagrama de clases de UML 9.
Para detallar la interacción entre los objetos durante la ejecución se puede utilizar diagramas
de comunicación y diagramas de secuencia. Una interacción de estas es la expresión de un
algoritmo global (más general) que afecta a distintos métodos.
• Finalmente hay que completar la parte privada de las clases:
– qué variables y métodos privados tiene
– cuál es el algoritmo de cada método

9 Aunque nosotros utilizaremos nuestros propios diagramas


Programación G-ISTSI 76

3.2
El diseño es lo importante
• La parte pública de una clase se llama interfaz.
• El interfaz define cómo se usa la clase. Es decir, qué contructores y qué metodos tiene; y para
cada uno se indica qué valores recibe y devuelve. Por ejemplo:

• En Java, el anterior interfaz se expresa así (no incluye los constructores):


public interface CocheInterfaz {
public String dimeMatricula ();
public void acelera (int incremento);
public int dimeVelocidad () ;
} // interface
Programación G-ISTSI 77

• Aún antes de implementar la clase, se debe escribir código que utilice sus métodos para hacerse
una idea su usabilidad:
Coche c1;

c1 = new Coche ("1234ABC");

c1.acelera(90);

int v = c1.dimeVelocidad();

c1.acelera (-10);

System.out.println ("el coche " + c1.dimeMatricula());


System.out.println ("va a la velocidad " + c1.dimeVelocidad());
Programación G-ISTSI 78

3.3
Implementar un diseño
Si hemos diseñado la interfaz de una clase, antes de implementarlo hay que decidir qué hay en la
parte privada. Para el diseño anterior es sencillo.

A continuación habría que decidir los algoritmos apropiados para cada método. En este caso son
triviales y podemos escribir directamente el código 10.

10 Pero en general esto no es así


Programación G-ISTSI 79

La implementación de la clase Coche se escribe en un fichero llamado Coche.java y es la


siguiente.
public class Coche {

private String laMatricula;


private int laVelocidad;

public Coche (String matricula) {


// constructor
this.laMatricula = matricula;
this.laVelocidad = 0;
} // ()

public String dimeMatricula() {


return this.laMatricula;
} // ()

public void acelera(int incremento) {


this.laVelocidad = this.laVelocidad + incremento;
if ( this.laVelocidad < 0) {
this.laVelocidad = 0;
Programación G-ISTSI 80

}
} // ()

public int dimeVelocidad() {


return this.laVelocidad;
} // ()

} // class

Analizar en el código de antes y comparar con el diseño:

• public – private
• perfil de cada método: valores de entrada y valor de retorno
• código de cada método. return
Programación G-ISTSI 81

Si hemos escrito el interfaz CocheInterfaz es conveniente


public class Coche
implements CocheInterfaz {
...
}

(El compilador comprueba que la implementación tiene los mismo métodos que el interfaz).
Programación G-ISTSI 82

3.4
Otro ejemplo: la clase Punto
• Diseño de la clase Punto

• Ejemplo de uso:
Punto p1 = new Punto();
Punto p2 = new Punto(3.0, 4.0);
Punto p3 = new Punto(1.2, 3.4);
double r;

r = p3.getY();
r = p1.distancia (p2);
p1 = p2.suma (p3);
Programación G-ISTSI 83

• Esquema de la llamada al método distancia


Programación G-ISTSI 84

• Implementación de la clase Punto (en fichero Punto.java)


public class Punto {

private double x;
private double y;

public Punto () {
x = 0.0;
y = 0.0;
} // ()

public Punto (double x, double y) {


this.x = x;
this.y = y;
} // ()

public double getX () {


return x;
} // ()
Programación G-ISTSI 85

public double getY () {


return y;
} // ()

public double distancia (Punto otro) {


double dx = x - otro.x;
double dy = y - otro.y;
return Math.sqrt (dx*dx + dy*dy);
} // ()

public Punto suma (Punto otro) {


Punto nuevo = new Punto(x+otro.x, y+otro.y);
return nuevo;
} // ()
} // class
Programación G-ISTSI 86

3.5
Relación entre el diseño de un método, su perfil, y la forma de llamarlo
Programación G-ISTSI 87

3.6
Reglas de acceso publico/privado

• Se puede acceder a la parte privada sólo desde código escrito dentro de la misma clase.
• Las variables auxiliares sólo son accesibles desde el propio método en que están.
Programación G-ISTSI 88

3.6.1
Los campos son privados
Los campos guardan el estado de un objeto y han de ser siempre privados. Por ejemplo, el campo
laVelocidad de la clase Coche

• Si los campos fueran públicos:


– En cualquier parte del programa podría escribirse c1.laVelocidad = 30; un cambio en
el nombre del campo afectaría a todo el programa.
– No se tiene control sobre qué se hace con ellos: no se podría evitar absurdos como
c1.laVelocidad = -50;
Programación G-ISTSI 89

• Si los campo son privados


– Los cambios sólo afectan al código de la clase.
– Podemos decidir qué acceso dar con métodos get() / set(): sólo lectura, sólo escritura,
lectura y escritura.
– Aunque demos acceso de escritura no es lo mismo que el campo sea público: con el método
podemos controlar qué valores se guardan (como hace acelera()).
Programación G-ISTSI 90

4
Introducción a la algorítmica
Problema algorítmico Relación entre unos datos de entrada y otros de salida establecida me-
diante las condiciones que deben cumplir los datos de salida.
Los datos de salida son así llamados porque dependen de los datos de
entrada.

Algoritmo secuencia de pasos no ambigua que soluciona un problema en un tiempo


finito.
Programación G-ISTSI 91

• Así pues, los datos de salida de un problema se calculan a partir de los de entrada. Un algoritmo
es una de las formas en que el cálculo puede realizarse.
• La única exigencia para los pasos de un algoritmo es que sean no ambiguos.
En ciertos contextos no hay inconveniente en utilizar pasos “muy generales”. En cambio, en
otras ocasiones se necesitará que los pasos sean “muy concretos”.
Por ejemplo, un paso (muy general) de un algoritmo puede ser “calcular la derivada del
polinomio p(x)”. En otro momento, tal vez haya que tomar ese paso como un sub-problema
y refinarlo en pasos más elementales como operaciones aritméticas simples (sumas, ...).
• Los algoritmos son independientes del lenguaje de programación: un algoritmo puede imple-
mentarse de formas distintas en el mismo o en diferentes lenguajes de programación.
• Para definir un problema indicaremos
– un nombre que identifique el problema
– cada uno de los datos de entrada, incluyendo de qué tipo es y si debe cumplir alguna
condición
– cada uno de los datos de salida, incluyendo de qué tipo es y las condiciones que debe
cumplir
Opcionalmente pueden incluirse descripciones adicionales y algún ejemplo.
Programación G-ISTSI 92

• Pasos para encontrar un algoritmo 11 que resuelva un problema dado:


1. Entender el problema con la ayuda de ejemplos de datos de entrada y salida concretos.
2. Pensar (idea feliz) y aplicar sobre unos datos de entrada concretos unos pasos (aunque
sean generales) que permitan obtener los datos de salida.
3. Repetir el paso anterior con más ejemplos para comprobar que los pasos funcionan siempre.
4. Resumir los pasos utilizados antes, cambiando valores concretos por variables y, si las hay,
repeticiones desarrolladas por repeticiones compactas.
5. si se considera necesario, refinar los pasos demasiados generales, tomándolos como sub-
problemas
6. Comprobar mediante una traza que el algoritmo es correcto. Una traza es aplicar el algo-
ritmo literalmente y paso por paso a unos datos de entrada.
Adicionalmente y en un contexto formal más avanzado habría que demostrar matemáticamente
la corrección del algoritmo y calcular su coste computacional.

11 Es decir: algoritmo para encontrar un algoritmo :-) .


Programación G-ISTSI 93

4.1
Ejemplos

4.1.1
Máximo común divisor (mcd)
• Definición del problema:
– Datos de entrada:
a : Entero, a > 0
b : Entero, b > 0
– Datos de salida:
m : Entero, m = max{s|a mod s = 0 ∧ b mod s = 0}
Es evidente la relación que hay entre la definición de un problema y una función 12 que lo
resuelva.

12 En Java: método estático.


Programación G-ISTSI 94

• Ejemplos 1−1→0
a = 8, b = 6 ⇒ m = 2 • Observamos que
a = 13, b = 4 ⇒ m = 1 – se repite la operación
a = 21, b = 14 ⇒ m = 7 may − men → r
• ¡Idea! aplicar restas. – Inicialmente
– si a = 8, b = 6 may ← max(a, b)
8−6→2 men ← min(a, b)
6−2→4 – Después de realizar may − men → r, y
4−2→2 para la vez siguiente
2−2→0 may ← max(men, r)
– si a = 13, b = 4 men ← min(men, r)
13 − 4 → 9 – Dejamos de repetir la resta cuando
9−4→5 may = men o equivalentemente
5−4→1 repetimos mientras may 6= men
4−1→3 – Al dejar de repetir may (o men) es la
3−1→2 solución.
2−1→1
Programación G-ISTSI 95

Por tanto, el algoritmo para mcd(a, b) → m es:


may ← max(a, b)
men ← min(a, b)
mientras may 6= men
may − men → r
may ← max(men, r)
men ← min(men, r)

m ← may
El paso may ← max(men, r) (y similarmente min()) puede refinarse
si men > r

may ← men

si no

may ← r
Programación G-ISTSI 96

4.1.2
sin(x)
• Definición del problema:
– Datos de entrada:
a : Real
– Datos de salida:
s : Real, s = longitud del cateto opuesto al ángulo a en un triángulo rectángulo de
hipotenusa = 1.
Una función para resolver este problema tendrá el siguiente diseño.

• En esta ocasión el algoritmo no es fácil y recurrimos a una fórmula matemática 13:



X (−1)i 2n+1
a
i=0
(2i + 1)!

13 Basada en las series de Taylor.


Programación G-ISTSI 97

• No hay que asustarse ante una fórmula como la anterior. Es muy sencillo entenderla si la
escribimos “desplegada”:
a3 a5 a7 a9
a− + − + ...
3! 5! 7! 9!
• Es importante darse cuenta que la anterior fórmula es el algoritmo. Observemos que
– cada sumando es un cálculo de la forma
potencia
signo ×
f actorial

– Tras haber calculado un sumando, se acumula al resultado que se tiene hasta el momento
– Inicialmente signo = 1, potencia = a y f actorial = 1
– Para el cálculo del siguiente sumando hay que cambiar
signo → −signo
potencia → potencia · a · a
f actorial → f actorial ·2i·((2i)+1) (si tenemos que calcular el sumando i ∈ [0, 1, 2, . . .])
Programación G-ISTSI 98

Por tanto, el algoritmo para sin(a) → s es:


resultado ← 0
signo ← 1
potencia ← a,
f actorial ← 1
i
0 −−−−−−−−−−−−−→ N
resultado ← resultado + (signo × potencia/f actorial)

signo ← −signo
potencia ← potencia · a · a
f actorial ← f actorial · 2i · ((2i) + 1)

s ← resultado
N es una constante con la que elegimos la cantidad de sumandos que queremos calcular.
Programación G-ISTSI 99

4.1.3
Ordenar
• Definición del problema:
– Datos de entrada:
numeros : lista de N números enteros. El i-ésimo entero puede consultarse con numeros[i]
(i ∈ [0, 1, . . . , N − 1]).
– Datos de salida:
numeros: lista de N números enteros (los mismos que los de entrada), cumpliendo ahora
que
numeros[0] ≤ numeros[1] ∧
numeros[1] ≤ numeros[2] ∧
numeros[2] ≤ numeros[3] ∧
numeros[3] ≤ numeros[4] ∧
... ∧
numeros[N − 3] ≤ numeros[N − 2] ∧
numeros[N − 2] ≤ numeros[N − 1]
es decir
numeros[i] ≤ numeros[i + 1] (i ∈ [0, 1, . . . , N − 2]).
Programación G-ISTSI 100

Una función para resolver este problema tendrá el siguiente diseño.

• Hay bastantes algoritmos para ordenar números. Veamos algunos.

4.1.3.1
Algoritmo de selección
• Seguramente sea éste el más intuitivo: se basa en buscar el menor y ponerlo al principio de
la lista. Luego, buscar el segundo menor y ponerlo en el segundo lugar de la lista, buscar el
tercer menor y ponerlo en el tercer lugar de la lista, etc.

Nos basaremos en la anterior descripción para desarrollar el algoritmo.

• En primer lugar, dado que hay que trabajar con una única lista (numeros), sus elementos
deben reordenarse in-situ. Por eso “poner al principio” significa intercambiar la posición de
dos elementos. De ahí que “buscar el menor” sea en realidad “buscar dónde está el menor”.
• Si por ejemplo hemos de “buscar dónde está el tercer menor” no hace falta mirar en toda la
lista. Si ya hemos ordenado el primero y el segundo, nos bastará con buscar a partir de la
tercera posición. Además, el intercambio será con la casilla a partir de la que hemos empezado
a buscar.
Programación G-ISTSI 101

 Podemos esbozar una primera versión del algoritmo:


posM enor ← buscar dónde está el menor empezando en la casilla 0
intercambiar las posiciones 0 y posM enor
posM enor ← buscar dónde está el menor empezando en la casilla 1
intercambiar las posiciones 1 y posM enor
posM enor ← buscar dónde está el menor empezando en la casilla 2
intercambiar las posiciones 2 y posM enor
...
posM enor ← buscar dónde está el menor empezando en la casilla N − 2
intercambiar las posiciones N − 2 y posM enor
 Es evidente que lo anterior es un bucle desplegado. Si lo plegamos obtenemos:
i
0 −−−−−−−−−−−−−→ N − 2
posM enor ← buscar dónde está el menor empezando en la casilla i
intercambiar las posiciones i y posM enor

 Ahora hemos de refinar “buscar dónde está el menor ...” y “intercambiar ...”.
Programación G-ISTSI 102

 Para conseguir
posM enor ← buscar dónde está el menor empezando en la casilla i
se compara cada elemento con un candidato a posM enor. Cuando se encuentre una casilla con
un elemento menor que numeros[posM enor] cambiaremos el candidato. Es decir:
posM enor ← i // el primer candidato es i
si numeros[posM enor] > numeros[i + 1], posM enor ← i + 1
si numeros[posM enor] > numeros[i + 2], posM enor ← i + 2
...
si numeros[posM enor] > numeros[N − 1], posM enor ← N − 1
Programación G-ISTSI 103

 podemos plegar el anterior fragmento con este bucle:


// buscar donde esta elmenor empezando en la casilla i
posM enor ← i
j
i+1 −−−−−−−−−−−−−→ N − 1
si numeros[posM enor] > numeros[j]

posM enor ← j
Programación G-ISTSI 104

 Para realizar el intercambio de las posiciones posM enor e i se necesita un auxiliar:


aux ← numeros[posM enor]
numeros[posM enor] ← numeros[i]
numeros[i] ← aux
Programación G-ISTSI 105

 Uniendo todas las piezas tenemos el algorimo completo de ordenación por selección:
i
0 −−−−−−−−−−−−−→ N − 2
//posM enor ← buscar dónde está el menor empezando en la casilla i
posM enor ← i
j
i+1 −−−−−−−−−−−−−→ N − 1
si numeros[posM enor] > numeros[j]

posM enor ← j

// intercambiar las casillas i y posM enor


aux ← numeros[posM enor]
numeros[posM enor] ← numeros[i]
numeros[i] ← aux
Programación G-ISTSI 106

4.1.3.2
Algoritmo de la burbuja
 Este algoritmo se basa en comparar casillas consecutivas e intercambiar su contenido si no están
correctamente ordenadas. En concreto:
comparar casillas 0 y 1, e intercambiar si no están ordenadas
comparar casillas 1 y 2, e intercambiar si no están ordenadas
comparar casillas 2 y 3, e intercambiar si no están ordenadas
...
comparar casillas N − 2 y N − 1, e intercambiar si no están ordenadas
 Al realizar lo anterior una vez, podemos comprobar que un número va subiendo como una
burbuja mientras encuentra números menores en su camino. Al final, el mayor de todos habrá
llegado al final.
Programación G-ISTSI 107

 Vamos a denotar con el nombre


“hacer la burbuja de 0 a i”
a estos pasos:
comparar casillas 0 y 1, e intercambiar si no están ordenadas
comparar casillas 1 y 2, e intercambiar si no están ordenadas
...
comparar casillas i y i + 1, e intercambiar si no están ordenadas
Como lo anterior es un bucle desplegado podemos plegarlo de la siguiente forma:
// hacer la burbuja de 0 a i
j
0 −−−−−−−−−−−−−→ i
si numeros[j] > numeros[j + 1]

intercambiar casillas j y j + 1
Programación G-ISTSI 108

 Ya hemos visto que al realizar la burbuja una vez, llevamos el mayor al final. Si lo volvemos a
hacer, conseguiremos que “el segundo mayor” llegue a la penúltima casilla, y así sucesivamente.
 Por tanto, para ordenar la lista completa haremos la burbuja terminando cada vez una casilla
antes (porque cada vez tenemos más números ordenados en la parte final).
hacer la burbuja de la casilla 0 a la N − 2
hacer la burbuja de la casilla 0 a la N − 3
hacer la burbuja de la casilla 0 a la N − 4
...
hacer la burbuja de la casilla 0 a la 2
hacer la burbuja de la casilla 0 a la 1
hacer la burbuja de la casilla 0 a la 0
Si plegamos el anterior bucle obtenemos:
i
N-2 −−−−−−−−−−−−−→ 0
hacer la burbuja de la casilla 0 a la i
Programación G-ISTSI 109

 Sólo queda unir las dos partes para obtener el algoritmo de la burbuja completo:
i
N-2 −−−−−−−−−−−−−→ 0
// hacer la burbuja de la casilla 0 a la i
j
0 −−−−−−−−−−−−−→ i
si numeros[j] > numeros[j + 1]

intercambiar casillas j y j + 1
Programación G-ISTSI 110

4.1.3.3
Algoritmo de inserción
 Supongamos que la lista de números tiene una parte (la izquierda) ordenada con respecto su
contenido. Por ejemplo:
2, 3, 8, 9 k 7, 1, 5
Como se ve, la parte 2, 3, 8, 9 esta ordenada con respecto a sí misma.
 Para continuar con la ordenación, tendremos que insertar el 7 entre el 3, 8; y para logralo
usaremos el algoritmo de la burbuja empezando donde está el 7 y yendo hacia la izquierda: al
comparar 9 y 7 los intercambiamos, al comparar 8 y 7 los intercambianos, al comparar 3 y 7 ya
no hay intercambio: el 7 ha quedado donde le corresponde, y la lista será
2, 3, 7, 8, 9 k 1, 5
con lo que la parte ordenada ha crecido. Ahora habría que seguir con la inserción del 1, y luego
la del 5.
Programación G-ISTSI 111

 Vamos a generalizar la anterior idea.


Si al principio la lista está completamente desordenada ¿cuál es la parte ordenada a la izquierda?
Evidentemente sólo la primera casilla: la 0. (Este podría ser un ejemplo: 8 k 3, 9, 2, 7, 1, 5)
Por eso, el primer número que intentaremos insertar será el de la casilla 1. (el 3 en el anterior
ejemplo).
 El primer esbozo del algoritmo es
insertar el número de la casilla 1
insertar el número de la casilla 2
insertar el número de la casilla 3
...
insertar el número de la casilla N-1
que plegado es:
i
1 −−−−−−−−−−−−−→ N − 1
insertar el número de la casilla i
Programación G-ISTSI 112

 Ahora se trata de escribir el algoritmo “insertar el número de la casilla i” Para que sea más fácil
pensemos en un ejemplo concreto:
2, 3, 8, 9 k 7, 1, 5
donde i = 4 y numeros[i] = 7
Haríamos:
comparar casillas 3 y 4, intercambiar si no estan ordenadas, parar si lo están
comparar casillas 2 y 3, intercambiar si no estan ordenadas, parar si lo están
comparar casillas 1 y 2, intercambiar si no estan ordenadas, parar si lo están
comparar casillas 0 y 1, intercambiar si no estan ordenadas, parar si lo están
En el ejemplo concreto 2, 3, 8, 9 k 7, 1, 5, hubiéramos parado al comparar las casillas 2 y 1 (es
decir el 7 con el 3).
Programación G-ISTSI 113

 En cualquier caso, al plegar los pasos anteriores (teniendo en cuenta que la casilla 4 es i)
tendremos:
// insertar el numero de la casilla i
j
i −−−−−−−−−−−−−→ 1
si numeros[j − 1] > numeros[j]

intercambiar casillas j − 1 y j

si no

j ← 0 // para que termine la repeticion


Programación G-ISTSI 114

 Finalmente el algoritmo de inserción completo es:


i
1 −−−−−−−−−−−−−→ N − 1
// insertar el número de la casilla i
j
i −−−−−−−−−−−−−→ 1
si numeros[j − 1] > numeros[j]

intercambiar casillas j − 1 y j

si no

j ← 0 // para que termine la repeticion


Programación G-ISTSI 115

4.2
Algoritmos y coste computacional
 Al ver los tres algoritmos de ordenación anteriores (selección, burbuja e inserción), surgen las
siguientes preguntas:

• Los tres tienen dos bucles anidados, se basan en comparar e intercambiar, y por su puesto
ordenan. ¿Son algoritmos esencialmente diferentes o son el mismo algoritmo?
La respuesta es que efectivamente son algoritmos distintos –aunque ordenen y se utilicen las
mismas operaciones básicas. Una forma intuitiva de verlo es representar gráficamente cómo
realizan las comparaciones.
Programación G-ISTSI 116

• Entonces, si son algoritmos diferentes, ¿son igual de rápidos o hay alguno más veloz?
Para medir “la velocidad” de un algoritmo no podemos utilizar un cronómetro. Con un re-
loj podemos medir la velocidad de la ejecución de un programa escrito en un lenguaje de
programación concreto y ejecutado en un ordenador particular.
Pero lo que queremos saber es cómo de rápido es un algoritmo, independientemente de si se
ejecuta en un ordenador viejo o nuevo, porque el algoritmo es en ambos casos el mismo.
 Así, lo que se hace para medir la velocidad de un algoritmo es contar la cantidad de
“operaciones elementales” que realiza. En nuestro caso las operaciones elementales son las
comparaciones y los intercambios. Además hay que fijarse en cuál es el punto del programa
que más “peso” aporta al algoritmo. Es evidente que como los tres algoritmos tienen dos
bucles anidados, las operaciones que más se repiten son las de dentro del bucle interno.
Programación G-ISTSI 117

 Como ejemplo recordemos el algoritmo de la burbuja:


i
N-2 −−−−−−−−−−−−−→ 0
// hacer la burbuja de la casilla 0 a la i
j
0 −−−−−−−−−−−−−→ i
// PARTE QUE MAS SE REPITE
si numeros[j] > numeros[j + 1]

intercambiar casillas j y j + 1

Vamos a contar cuántas veces se repite la “parte que más se repite”:


– cuando i = N − 2, j va de 0 a N − 2, es decir N − 1 veces
– cuando i = N − 3, j va de 0 a N − 3, es decir N − 2 veces
– cuando i = N − 4, j va de 0 a N − 4, es decir N − 3 veces
– ...
– cuando i = 2, j va de 0 a 2, es decir 3 veces
– cuando i = 1, j va de 0 a 1, es decir 2 veces
– cuando i = 0, j va de 0 a 0, es decir 1 vez
Programación G-ISTSI 118

Sumando lo anterior tenemos:

1 + 2 + 3 + ... + N − 3 + N − 2 + N − 1
=
N
X −1
i
i=1

=
N (N − 1)/2
=
1 2 1
N − N
2 2
Así pues, tenemos que 12 N 2 − 12 N es la cantidad de veces que se ejecuta la comparación más
repetida del algoritmo. Por simplicidad se dice que el coste es “del orden de N 2 ” que es el
término de mayor peso en la expresión.
Programación G-ISTSI 119

Formalmente, se puede demostrar que para N grandes las expresiones N 2 y 12 N 2 − 12 N sólo


2
difieren en un factor constante (calculando limn→∞ 1 n2n− 1 n ).
2 2
• Mediante un análisis similar al anterior, se puede ver que los otros dos algoritmos (selección
e inserción) tienen también un coste del orden de N 2 . ¿Hay algún algoritmo más rápido que
estos?
La respuesta es sí.

4.2.1
Algoritmo quicksort
 Este algoritmo se basa en tomar un número como referencia (pivote) y situar a su izquierda los
que sean menores o iguales que él y a su derecha los que sean mayores. Por ejemplo, si la lista
de números es
10, 15, 7, 3, 13, 18, 1, 4, 6, 8, 11, 12, 19, 5
y tomamos el 10 como pivote, entonces la reordenación dejaría la lista de esta forma:
5, 8, 6, 4, 1, 3, 7, 10 , 15, 13, 18, 11, 12, 19
 ¿Qué hemos conseguido? Ordenar el 10.
Ahora podemos seguir aplicando la ordenación de forma separada a cada sub-lista. Es decir
3, 1, 4, 5 , 8, 6, 7, 10 , 11, 13, 15 , 18, 19
Programación G-ISTSI 120

Pero al terminar esta fase hemos ordenado 2 números en vez de sólo 1. También hemos obtenido
4 sublistas. Lo cual significa que, si seguimos, ordenaremos 4 números en vez de 2, y obtendremos
8 sublistas.
 ¿Qué cuesta realizar una reordeanción?:
Cuando la lista tiene N elementos hay que hacer N comparaciones.
Cuando tenemos 2 sublistas de N/2 elementos, cada sublista necesita N/2 comparaciones. Como
son 2 sublistas, N/2 + N/2 da N comparaciones.
En general, aunque las sublistas tienen cada vez la mitad de elementos, también es cierto que hay
el doble de sublistas, con lo que todas las reordenaciones juntas suman siempre N comparaciones.
 La clave es que en cada fase ordenamos tantos números como sublistas hay. Así pues ¿cuántas
fases necesitamos realizar para ordenar todos los números? Vamos a contarlo:

• Fase 1, 1 sublista, 1 número ordenado. (N comparaciones)


• Fase 2, 2 sublistas, 2 números ordenados. 1 + 2 = 3 números ordenados en total (N compa-
raciones)
• Fase 3, 4 sublistas, 4 números ordenados. 1 + 2 + 4 = 7 números ordenados en total (N
comparaciones)
• Fase 4, 8 sublistas, 8 números ordenados. 1 + 2 + 4 + 8 = 15 números ordenados en total (N
comparaciones)
Programación G-ISTSI 121

• ...
• Fase i, 2i−1 sublistas, 2i−1 números ordenados. 1 + 2 + 4 + 8 + ... + 2i−1 = 2i − 1 números
ordenados en total (N comparaciones)

Es decir, con i fases ordenamos 2i números. ¿Cuántas fases necesitamos para ordenar N números?
Resolvamos i de la expresión 2i = N : i = log2 N . Es decir necesitamos log2 N fases.
 Finalmente, el coste del algoritmo quicksort es

N log2 N
(número de comparaciones en cada fase por número de fases).
 La velocidad del quicksort (N log2 N ) es abismalmente más rápida que la de los otros tres
algoritmos (N 2 ). Si hubiera que ordenar 1000 números y cada comparación costara un segundo,
el quicksort tardaría aproximadamente 6900 segundos, es decir casi 2 horas. Por su parte, los otros
algoritmos tardarían 1 millón de segundos: aproximadamente 11 días y medio.
Programación G-ISTSI 122

5
Relaciones entre clases
• En orientación a objeto, las relaciones básicas que pueden darse entre las clases que componen
un programa son:
– tener
– ser (herencia)
Programación G-ISTSI 123

Por ejemplo, si un Coche tiene un Motor y un Coche es un Vehiculo,


• en UML –utilizando un diagrama de clases, estas relaciones se representan así:

• y en Java, se escriben así:


public class Vehiculo {
...
}
public class Motor {
...
}
public class Coche extends Vehiculo {
...
private Motor elMotor;
...
}
Programación G-ISTSI 124

5.1
Tener
• La relación tener se da entre dos clases cuando una posee a la otra, habiendo los siguientes
matices:
– composición (Coche tiene un Motor)
– agregación (Coche tiene un Conductor)
– contener (Parking tiene Coches)
• Como hemos visto, la relación tener se expresa en Java declarando un campo.
public class Coche extends Vehiculo {
...
private Motor elMotor; // Coche tiene Motor
...
}

• Es obvio que si objeto A tiene a otro B, entonces A usará a B de alguna forma. Una forma
importante de uso es la delegación.
Programación G-ISTSI 125

5.2
Delegación
• La delegación se da cuando un objeto a (delegador) usa a otro b (delegado), para que éste
último haga todo o parte del trabajo de a.
• Evidentemente se trata de un patrón absolutamente fundamental y ampliamente usado.
• Por ejemplo, supongamos que un Coche tiene Motor, y que ambos tienen el método acelera().
Entonces, cada vez que se llame a Coche.acelera() éste llamará a Motor.acelera():
public class Coche extends Vehiculo {
...
private Motor elMotor; // Coche tiene Motor
...
public void acelera (int incremento) {
elMotor.acelera( incremento );
}
...
}
Programación G-ISTSI 126

5.3
Herencia
• La herencia o relación ser un es un mecanismo 14 que permite que una clase aproveche el código
de otra.

Si Coche hereda de Vehiculo también diremos que


– Coche es un Vehiculo
– Coche deriva de Vehiculo
– Coche puede sustituir a Vehiculo
– Coche es la clase derivada, la clase hija, la subclase
– Vehiculo es la clase base, la clase padre, la superclase
– Coche es más especializada que Vehiculo
– Vehiculo es más general que Coche
– Si un objeto es de tipo Coche también es de tipo Vehiculo

14 Distinto de la delegación.
Programación G-ISTSI 127

• Como hemos visto, la herencia se expresa en Java con la cláusula extends. En Java la herencia
es simple: sólo se puede heredar de una clase.
• La primera acción de cualquier constructor de una clase derivada debe ser llamar a un cons-
tructor de la clase base mediante super.
public class Coche extends Vehiculo {
...
public Coche (String matricula) {
super (matricula);
}
...
}

Hemos supuesto que la clase base gestiona la matrícula:


public class Vehiculo {
...
String laMatricula;
public Vehiculo (String matricula) {
laMatricula = matricula;
}
...
}
Programación G-ISTSI 128

5.3.1
Principales motivos para utilizar herencia
• Factor común.
Si varias clases (p.ej. Coche, Moto, Bicicleta) tienen un método común (dimeVelocidad()),
podemos escribir sólo una vez ese método en una clase base (Vehiculo) y hacer que las clases
(Coche, Moto, Bicicleta) deriven de ella (Vehiculo).
Programación G-ISTSI 129

• Especialización
Si tengo una clase javax.swing.JButton 15 y en mi programa quiero que los botones tengan
un estilo propio (p. ej. fondo azul y letra naranja), lo correcto es
public class MiBoton extends JButton {
public MiBoton (String texto) {
super (texto);
super.setBackground (Color.BLUE);
super.setForeground (Color.ORANGE);
... // mas particularidades que pueda necesitar
}
}

para poder hacer fácilmente


MiBoton b1 = new MiBoton ("Aceptar");
MiBoton b2 = new MiBoton ("Cancelar");

15 Clase real de la biblioteca de interfaces gráficas de Java que representa a un botón.


Programación G-ISTSI 130

 Si no especializamos JButton en MiBoton tendríamos que escribir el siguiente código con-


fuso:
JButton b1 = new JButton ("Aceptar");
JButton b2 = new JButton ("Cancelar");
b1.setBackground (Color.BLUE);
b1.setForeground (Color.ORANGE);
b2.setBackground (Color.BLUE);
b2.setForeground (Color.ORANGE);
Programación G-ISTSI 131

• Polimorfismo.
Si para realizar una determinada tarea (p.ej. viajar) nos sirve cualquier clase (Coche o Moto)
que tenga unos determinados requisitos (que sea un Vehiculo y que tenga el método acelera()),
entonces, la herencia permite garantizar que todo encaja: cualquier objeto de tipo Coche o
Moto será de tipo Vehiculo y tendrá el método acelera() porque lo habrán heredado.
Así podremos hacer:
void viajar (Vehiculo v) {
v.acelera(120);
}
...

void f() {
Coche c = new Coche ("1234ABC");
Moto m = new Moto ("78DF");

viajar (c);

viajar (m);
}
Programación G-ISTSI 132

5.3.2
Cambios de implementación
• No es raro que una clase derivada necesite cambiar la implementación de alguno de los métodos
heredados.
• Para ello se sobrescribe el método heredado: con exactamente el mismo perfil y diferente
código. Por ejemplo, si tenemos la clase base Vehiculo
public class Vehiculo {
private String laMatricula;
private int laVelocidad;
public Vehiculo (String matricula) {
// constructor
this.laMatricula = matricula;
this.laVelocidad = 0;
} // ()
public void acelera(int incremento) {
laVelocidad = (laVelocidad+incremento < 0 ? 0 : laVelocidad+incremento);
} // ()
...
} // class

y en la clase derivada Coche queremos “trucar” el acelerador, haríamos:


Programación G-ISTSI 133

public class Coche extends Vehiculo {


Coche (String matricula) {
super (matricula);
}
public void acelera(int incremento) { // sobrescribimos Vehiculo.acelera()
super.acelera (incremento+10);
}
} // class
Programación G-ISTSI 134

5.3.3
Contratos de implementación
• Es útil disponer de un mecanismo que verifique que un diseño es implementado correctamente.
• Para ello, en Java, podemos escribir un interfaz que define una lista de métodos que deseamos
que sean implementados.
• Un interfaz es un contrato.
Por ejemplo, podríamos decidir que Vehiculo en vez de ser una clase ordinaria fuera un
interfaz con los métodos que queremos que tenga cualquier vehículo.
public interface Vehiculo {
String dimeMatricula ();
int dimeVelocidad ();
void acelera (int incremento);
}
Programación G-ISTSI 135

De esta forma, al escribir las clases Coche o Moto haremos


public class Coche public class Moto
implements Vehiculo { ... } implements Vehiculo { ... }

y entonces
– el compilador emitirá un error si las clases Coche o Moto no tienen los métodos del interfaz
Vehiculo
– un objeto de tipo Coche o Moto será también de tipo Vehiculo.
– podremos usar el interfaz como requisito en un contexto de polimorfismo. Recordemos:
void viajar (Vehiculo v) {
v.acelera(120);
}
void f() {
Coche c = new Coche("1234ABC");
Moto m = new Moto("78DF");
// c y m son de tipo Vehiculo, por tanto:
viajar (c);
viajar (m);
}
Programación G-ISTSI 136

• La relación entre el interfaz Vehiculo y las clases Coche y Moto se puede representar así:
Programación G-ISTSI 137

5.3.3.1
Implementaciones parciales
• Hay ocasiones en que se necesita una clase parcialmente implementada: algunos métodos
tienen código y otros no.
• De esta forma, la clase sirve de contrato (obliga a implementar los métodos sin código) pero
también deja en herencia los métodos implementados.
• Una clase parcialmente implementada se llama clase abstracta. Los métodos sin código se
denominan abstractos.
• Un clase abstracta no se puede instanciar. Hay que escribir una clase que derive de ella y
complete los métodos abstractos.
• En Java, un interface sería un clase asbstracta con todos los métodos abstractos.
Programación G-ISTSI 138

• Si por ejemplo, queremos escribir una clase Vehiculo que


– por un lado implemente la gestión de la matrícula para que puede ser heredada y compartida
– por otro lado, queremos obligar a que toda clase derivada implemente los métodos acelera()
y dimeVelocidad()
entonces, en Java escribiríamos:
public abstract class Vehiculo {
private String laMatricula;
public Vehiculo (String matricula) {
this.laMatricula = matricula;
} // ()
public String dimeMatricula() {
return laMatricula;
}
public abstract void acelera(int incremento);
public abstract int dimeVelocidad();
...
} // class
Programación G-ISTSI 139

• El diseño del anterior se puede representar así:


Programación G-ISTSI 140

5.3.4
Sustituibilidad: herencia correcta
• Cuando una clase hereda de otra y rescribimos un método (ver 5.3.2) se corre el peligro de que
la nueva implementación rompa el contrato que se tenía hasta ese momento, y el programa
deje de funcionar correctamente.
• Por ejemplo, hasta ahora hemos utilizado el método Coche.acelera() tanto para aumentar
la velocidad como para reducirla (dando un valor negativo).
Si creamos una nueva clase Bolido que derive de Coche, en la cual:
– cambiamos el método acelera() para que sólo aumente la velocidad
– añadimos un método frena() para reducir la velocidad
entonces, tenemos un problema: un objeto Bolido pese a ser de tipo Coche no puede ser
utilizado en el programa porque nada del código anterior sabe de la existencia del método
frena() y lo que es peor, utilizan acelera() con valores negativos para frenar (cosa que
Bolido no soporta).
Programación G-ISTSI 141

• Así pues, el criterio de corrección en la herencia es la sustituibilidad: si Bolido hereda de


Coche, donde se pueda poner un objeto de tipo Coche se podrá poner un objeto de tipo
Bolido. Por eso, si sobrescribimos un método:
– no puede añadir pre-condiciones
– no puede eliminar post-condiciones
(no puede hacer menos cosas que las que hacía, ni pedir más condiciones para ejecutarse que
antes).
• En línea con esto, puede resultar chocante, aunque es bien cierto que
un Parking<Coche> no es un Parking<Vehiculo>.
(aunque un Coche sea un Vehiculo).
¿Por qué? Porque un Parking<Coche> no puede sustituir a un Parking<Vehiculo>: en
un Parking<Vehiculo> se puede guardar Motos, pero en un Parking<Coche> no se puede
guardar una Moto.
Programación G-ISTSI 142

5.3.5
Objetos polifacéticos
• Al utilizar la herencia un objeto tiene varios tipos.
• Ya hemos visto que si Coche hereda de Vehiculo, entonces, un objeto de tipo Coche es
también de tipo Vehiculo.
• Más aún, si Coche implementa los interfaces CocheDeTrabajo y CocheFamiliar entonces,
un objeto de tipo Coche es también de tipo CocheDeTrabajo y CocheFamiliar.
• La anterior situación puede representarse así:
Programación G-ISTSI 143

• Hay que tener en cuenta que un objeto de tipo Coche es un único objeto, pero dado el anterior
diseño tiene varias facetas y según cómo se le referencie se puede ver unas u otras:

Si apuntamos al objeto
– con una referencia de tipo Coche veremos todos los métodos (los de Vehiculo, CocheFammiliar,
CocheDeTrabajo y otros que pudiera tener la clase Coche).
– con una referencia de tipo Vehiculo sólo veremos los métodos dimeMatricula(), dimeVelocidad()
y acelera().
Programación G-ISTSI 144

– con una referencia de tipo CocheFamiliar sólo veremos los métodos salirDePaseo() y
hacerLaCompra().
– con una referencia de tipo CocheDeTrabajo sólo veremos los métodos irAlTrabajo() y
estarEnUnAtasco().
Programación G-ISTSI 145

5.3.5.1
Casting
• Hemos visto que un objeto es a la vez de varias clases y que según el tipo de la referencia con
que se le apunte podremos utilizar unos u otros métodos.
• Por ejemplo si apuntamos a un objeto de tipo Coche con una referencia de tipo CocheFamiliar
sólo veremos los métodos salirDePaseo() y hacerLaCompra().
• Pero nosotros sabemos que tiene otros métodos. Si queremos utilizarlos hemos de hacerlo
mediante una referencia de tipo diferente.
• La asignación de una referencia de un tipo a otro, en general, no está permitida, excepto que
se utilice un casting: un aviso escrito por nosotros para que el compilador no dé error.
• El casting no afecta a los objetos, sino a las referencias.
Programación G-ISTSI 146

 Tipos de casting:

• El casting es innecesario si asignamos algo con un tipo de menor precisión a uno con mayor
precisión. Por ejemplo:
int a = 3;
double x;

x = a; // de entero a real, OK

En cambio en el sentido inverso hay que escribir un casting para dar a entender que somos
conscientes de la pérdida de información:
a = (int) x; // de real a entero, necesito casting

• upcasting El casting también es innecesario cuando asignamos una referencia de un tipo


derivado (como Coche) a un tipo base (como Vehiculo): esta garantizado que la referencia
de tipo Coche apuntará a un objeto de tipo Vehiculo.
Coche c;
Vehiculo v;
...
v = c; // OK
Programación G-ISTSI 147

• downcasting El casting es necesario cuando asignamos una referencia de tipo base (Vehiculo
a una de tipo derivado (Coche): la referencia de tipo Vehiculo podría no estar apuntando a
un objeto Coche.
Coche c;
Vehiculo v;
...
c = (Coche) v; // casting necesario

• También se necesita un casting si asignamos una referencia de tipo CocheFamiliar a una


de tipo CocheDeTrabajo (y asumiendo que estamos apuntado a un objeto Coche que es de
ambos tipos).
CocheFamiliar f;
CocheDeTrabajo t;
...
t = (CocheDeTrabajo) f; // casting necesario
Programación G-ISTSI 148

 instanceof

• Con un casting sólo conseguimos que el compilador no se queje.


• Pero al ejecutar un programa como
Coche c;
Vehiculo v;
...
c = (Coche) v; // casting necesario

si v no apunta a un objeto Coche sino Moto, se producirá un error.


• Podemos comprobar en ejecución el tipo del objeto apuntado con instanceof
Coche c;
Vehiculo v;
...
if (v instanceof Coche) {
c = (Coche) v;
} else {
// hacer otra cosa
}
Programación G-ISTSI 149

5.4
Tenencia y delegación vs. herencia
• Recordemos que la relación tener permite construir un objeto uniendo partes (otros objetos)
y utilizándolos para que realicen el trabajo (delegación).
En la herencia, el código de un objeto base entra a formar parte intrínseca del objeto derivado.
• Es un error común “sobre-utilizar” la herencia, es decir, utilizarla cuando no se necesita o de
forma errónea.
• La herencia sólo debe utilizarse si está claro que un objeto derivado es también de tipo base:
Coche es Vehiculo pero Motor no. Coche tiene Motor.
• En general, es preferible utilizar la delegación a la herencia. En la herencia, la clase derivada
conoce muchos detalles de la parte privada de la clase base, y acaban apareciendo dependencias
innecesarias: lo deseable es utilizar sólo las partes públicas.
• Si nos centramos demasiado en la herencia terminaremos sobre-diseñando: creando árboles de
herencia innecesariamente amplios.
Por ejemplo, podríamos diseñar clases como VehiculoAMotor, VehiculoSinMotor, CocheFamiliar,
Bolido, etc. que aunque las hayamos utilizado en los ejemplos, puede que en un caso prácti-
co sean no sólo innecesarias sino que introduzcan complejidad evitable. Si el programa puede
realizarse con Vehiculo, Coche y Moto, mejor: Keep it as simple as possible, no more.
Programación G-ISTSI 150

5.5
Clases anónimas
 Hay ocasiones en que se necesita modificar ligeramente una clase existente (rescribir uno o dos
métodos) para crear un objeto de forma puntual.
Tener que escribir la nueva clase en un fichero separado resulta incómodo.
 Java permite en un sólo paso crear e instanciar una nueva clase “in situ” 16. La nueva clase se
crea a partir de otra existente –herencia– o incluso a partir de un interfaz.
 Un ejemplo real.
unBoton.addActionListener (
new ActionListener () {
public void actionPerformed (ActionEvent ev) {
System.out.println ("soy el boton hola: " + valor);
} // actionPerformed()
} // class
); // addActionListener

16 Justo allí donde se necesita.


Programación G-ISTSI 151

• unBoton es un botón gráfico (de tipo JButton)


• su método addActionListener() es para decirle qué código ha de ejecutarse cuando sea
pulsado.
• addActionListener() espera recibir un objeto que implemente el interfaz ActionListener
–lo cuál asegura que el objeto recibido implementará el método actionPerformed(), que es
el que el botón llamará al ser pulsado.
• En vez de escribir en un fichero aparte una clase que implemente el interfaz ActionListener
y luego crear un objeto de esa nueva clase, lo hacemos todo en un sólo paso:
new ActionListener () {
public void actionPerformed (ActionEvent ev) {
... // implementacion que queramos
} // actionPerformed()
} // classe anonima

• Nota: desde el código de un clase anónima se puede acceder campos de la clase pero no
variables locales del método en que se crea 17.

17 Porque el objeto de la clase anónima sobrevive al método.


Programación G-ISTSI 152

5.6
Un ejemplo: Formas
Veamos un diseño simplificado y parcial de lo que podría ser una aplicación de dibujo sencilla.
Programación G-ISTSI 153

Para entender y completar el diseño veamos el siguiente diagrama de interacción:


Programación G-ISTSI 154

(Interacción con otro formato)


Programación G-ISTSI 155

6
Excepciones
En 2.5 introdujimos las excepciones. Recordemos:

• Situación anómala que puede ocurrir en un programa y frente a la que puede reaccionarse
capturándola o traspasándola (para que sea capturada en otra parte del programa).
• Sabemos que un método puede fallar porque en su perfil aparece una cláusula throws. Por
ejemplo:
public static int parseInt(String s) throws NumberFormatException
Programación G-ISTSI 156

• Si deseamos capturar excepciones de métodos que puedan fallar, escribiremos un try-catch:


try {
// codigo habitual, con llamadas a metodos que pueden fallar
...
int a = Integer.parseInt("e24"); // fallara !
...
} catch (NumberFormatException e) {
// hacer algo si falla parseInt()
...
} catch (OtraExcepcion e) {
// hacer algo si se dispara OtraException
...
}

• Si no queremos capturar la excepción, sino traspasarla a quien nos llamó:


void miMetodo () throws NumberFormatException {
// codigo habitual, con llamadas a metodos que pueden fallar
...
int a = Integer.parseInt("e24"); // fallara !
...
}
Programación G-ISTSI 157

6.1
Escribir nuevas excepciones
• Escribir una nueva excepción debe estar justificado por el diseño del programa que estemos
desarrollando. Además, conviene comprobar si en la biblioteca estándar hubiera alguna excep-
ción que nos pueda servir.

1. Escribimos una clase que derive de Exception. Por ejemplo:


public class MiExcepcion extends Exception {
public MiExcepcion (String mensaje) {
super (mensaje);
}
}

Es muy simple: lo importante es elegir un nombre representativo para la clase (en vez de
MiExcepcion).
Es obligatorio escribir un constructor que reciba un string y que lo reenvie al código he-
redado (para que se lo guarde). Más tarde se podrá consultar ese mensaje con el método
getMessage().
En la mayoría de casos, sólo por el nombre de la excecpción sabremos qué ha pasado, sin
necesidad de utilizar getMessage(). Otro método disponible (heredado de Exception) es
Programación G-ISTSI 158

printStackTrace() que escribe en la consola la cadena de llamadas desde el método que


falló por primera vez.
2. Aquel método donde pueda darse la anomalía de la que avisamos con nuestra excepción tendrá
este esquema:
public void miMetodo(...) throws MiExcepcion {
...
if ( he detectado el fallo ) {
throw new MiExcepcion ("hay un fallo");
}
...
}

3. El resto ya es conocido. Quien llame a miMetodo() podrá capturar o traspasar MiExcepcion.


Programación G-ISTSI 159

7
Programación genérica. Plantillas.
 Supongamos que queremos escribir una clase llamada Caja para guardar dos valores enteros.
class Caja {
private int primero;
private int segundo;
public Caja (int pri, int seg) {
primero = pri;
segundo = seg;
} // ()
int getPrimero () {
return primero;
} // ()
int getSegundo () {
return segundo;
} // ()
} // class

Es evidente que si en vez de dos enteros fueran dos números reales (o cualquier otro tipo), el
único cambio que necesita la clase Caja es reemplazar int por double.
Programación G-ISTSI 160

 Por este motivo aparecen las plantillas: meta-clases que se escriben de forma genérica depen-
diendo de un tipo T que más tarde se dirá cuál es. Así, con una plantilla se pueden crear fácilmente
muchas clases similares.
 En nuestro ejemplo, para conseguir Caja<T>: una caja genérica que contiene dos T, escribiremos
class Caja<T> {
private T primero;
private T segundo;
public Caja (T pri, T seg) {
primero = pri;
segundo = seg;
} // ()
T getPrimero () {
return primero;
}
T getSegundo () {
return segundo;
}
} // class

y la podremos utilizar así:


Programación G-ISTSI 161

Caja<Double> cd = new Caja<Double> (1.2, 3.4);

double a = cd.getPrimero ();


double b = cd.getSegundo ();

 Es importante destacar que para indicar qué tipo concreto es el parámetro T de una plantilla no
se puede utilizar tipos básicos (int, double, etc.). Hay que utilizar el envoltorio correspondente
(Integer, Double, etc. Ver paquete java.lang).
 El diseño de la plantilla Caja<T> es el siguiente:
Programación G-ISTSI 162

7.1
Exigiendo requisitos a un tipo de plantilla
 La plantilla Caja<T> anterior puede instanciarse con cualquier tipo, porque las operaciones que
Caja<T> hace con T son guardar y devolver, que son soportadas universalmente.
 ¿Qué ocurría si Caja<T> necesitara que T tuviera el método convertirAEntero()?
 Para obligar a que T tenga el método convertirAEntero(), se escribe un interfaz que exprese
esa obligación:
interface ConvertibleAEntero {
int convertirAEntero();
}

 A continuación se expresa la obligación en la plantilla:


class Caja<T extends ConvertibleAEntero> {
...
int sumalos () {
return primero.convertirAEntero() + segundo.convertirAEntero();
}
} // class

El método sumalos() es el que hace uso del método convertirAEntero().


Programación G-ISTSI 163

 Evidentemente, para poder utilizar la clase Caja<T extends ConvertirbleAEntero habrá


que escribir una clase que implemente el interfaz ConvertibleAEntero.
class Real implements ConvertibleAEntero {
private Double valor;
public Real (double v) {
valor = new Double (v);
}
public double getValor () {
return valor.doubleValue();
}
public int convertirAEntero () {
return valor.intValue();
}

} // class
Programación G-ISTSI 164

7.2
Interfaces plantilla
 Un interfaz también puede estar parametrizado y de hecho en muchas ocasiones necesitaremos
que lo esté.
 Volvamos al ejemplo de Caja<T> y su pongamos ahora que pretendemos añadirle un método
sumaGuardados() que devuelva la suma de lo que guarda:
public T sumaGuardados () {
return primero.suma(segundo);
}

 Como se ve, necesitamos que la clase T tenga el método suma(), que reciba algo de tipo T y
que (T + T → T ) devuelva T.
 Esto se consigue con el siguiente interfaz plantilla:
interface Sumable<T> {
public T suma (T otro)
}
Programación G-ISTSI 165

 Y con ese interfaz expresaremos la obligación


class Caja<T extends Sumable<T> > {
...
public T sumaGuardados () {
return primero.suma(segundo);
}
} // class

 Si quisierámos que el tipo T fuera la clase Real, haríamos que tuviera el método Real suma
(Real otro):
class Real implements Sumable<Real> {
...
Real suma (Real otro) {
...
}
} // class
Programación G-ISTSI 166

7.3
Una Caja<Coche> no es una Caja<Vehiculo>
 Efectivamente. Aunque Coche herede de Vehiculo una Caja<Coche> no es una Caja<Vehiculo>
porque Caja<Coche> no puede sustituir a Caja<Vehiculo>:
en una Caja<Vehiculo> podemos guardar un Coche y una Moto, pero en una Caja<Coche> sólo
podemos guardar coches.
 Entonces, si la clase base de Caja<Coche> no es de tipo Caja<Vehiculo>, ¿de qué tipo es?:
de tipo Caja<?>
 De esta forma se puede escribir un método que reciba cualquier tipo de Caja:
void metodoQueRecibeCualquierCaja (Caja<?> ca) {
...
}
Programación G-ISTSI 167

 Si queremos un método que reciba una caja que contega algo derivado de Vehiculo:
void miMetodo (Caja<? extends Vehiculo> caj) {
...
}

y también sirve
public static <T extends Vehiculo> void m1 (Caja<T> ca) {
...
}
Programación G-ISTSI 168

7.4
Algoritmos genéricos
 Hay un importante principio de diseño que dice que hay que separar un contenedor de los
algoritmos que se aplican sobre él.
Esto permite

• diseñar e implementar un contenedores genéricos de elementos (como ArrayList) de forma


independiente
• diseñar e implementar algoritmos genéricos aplicables a contenedores escritos previamente
Programación G-ISTSI 169

7.4.1
Algoritmo “para cada” sencillo
 Se trata de un método estático Para.cada() 18 que aplica una función genérica a cada elemento
de una colección.
 La definición es la siguiente:
//............................................
abstract class Funcion <T> {
public abstract void visita (T v);
public Object getResultado () { return null; }
} // class

//............................................
class Para {
public static <T> void cada (Collection <T> c, Funcion<T> f) {
for (T v : c) {
f.visita (v);
}
} // ()
} // class

18 El nombre de la clase Para y del método cada() forman un juego de palabras para que quede la frase “para cada”.
Programación G-ISTSI 170

 Un ejemplo de uso del método Para.cada() es este:


ArrayList<String> palabras = new ArrayList<String> ();
palabras.add ("hola");
palabras.add ("que");
palabras.add ("tal");

Funcion<String> fun = new Funcion<String> () {


private int total = 0;
public void visita (String s) {
total = total + s.length();
}
public Object getResultado() {
return total;
}
};

Para.cada (palabras, fun);

System.out.println (" suma de longitudes = " + visi.getResultado());


Programación G-ISTSI 171

 Como se ve, el anterior código, calcula la suma de longitudes de los strings guardados en el
vector palabras. Algo que puede hacerse simplemente con:
int total = 0;
for (String s : palabras) {
total = total + s.length();
}

 Sin embargo la versión Para.cada() tiene la pequeña ventaja de haber creado un objeto
Funcion<String> fun que engloba lo que se pretende hacer con cualquier string que éste reciba,
siendo fun fácilmente reutilizable, y utilizable: Para.cada (palabras, fun);
Programación G-ISTSI 172

7.4.2
Visitador
 Un visitador a una colección es esencialmente un método Para.cada() con la variación de que
permite aplicar una función distinta según el tipo concreto del elemento guardado en la colección.
A continuación vemos la definición del nuevo Para.cada().
//............................................
abstract class Visitador<T> {
public void Visita(T n) {
try {
this.getClass().getDeclaredMethod("hazlo", n.getClass()).invoke(this, n);
} catch (Exception ex) {
hazlo((T) n);
}
}
public void hazlo(T n) {
System.out.println("hazlo() para base " + n);
}
public Object getResult() {
return null;
}
} // class
Programación G-ISTSI 173

//............................................
class Para {
public static <T> void cada (Collection<T> c, Visitador<T> f) {
for (T v : c) {
f.Visita(v);
}
} // ()
} // class

 En el siguiente ejemplo vemos que que la colección es un ArrayList<Number> para la que


podemos especificar qué hacer con cada elemento guardado dependiendo de su tipo específico
(Double, Float o cualquier otro Number):
ArrayList<Number> numeros = new ArrayList<Number>();

numeros.add(new Double(1.2));
numeros.add(new Float(-1.2));
numeros.add(new Double(4.8));
numeros.add(new Float(-3.4));
numeros.add(new Long(123456));
numeros.add(new Short("14"));
Programación G-ISTSI 174

Para.cada(numeros, new Visitador<Number>() {


public void hazlo(Double n) {
System.out.println("hazlo() para double: " + n);
}
public void hazlo(Float n) {
System.out.println("hazlo() para float: " + n);
}
public void hazlo(Number n) {
System.out.println("hazlo() para Number: " + n);
}
});
Programación G-ISTSI 175

7.4.3
Buscador
 En este ejemplo mostramos como escribir una función genérica que busque en una colección los
elementos que cumplan Buscar.losQueCumplen() (o los que no: Buscar.losQueNoCumplen())
un predicado, produciendo una nueva colección.
He aquí las definiciones
//............................................
interface Predicado <T> {
boolean esCierto (T v);
} // class
//............................................
class Buscar {
public static <T> Collection<T> losQueCumplen (Collection <T> c, Predicado<T> pred) {
ArrayList<T> result = new ArrayList<T> ();
for (T v : c) {
if ( pred.esCierto (v) ) {
result.add (v);
}
} // for
return result;
} // ()
Programación G-ISTSI 176

public static <T> Collection<T> losQueNoCumplen (Collection <T> c, Predicado<T> pred) {


ArrayList<T> result = new ArrayList<T> ();
for (T v : c) {
if ( ! pred.esCierto (v) ) {
result.add (v);
}
} // for
return result;
} // ()

} // class

 El siguiente es un ejemplo de uso:


ArrayList<String> palabras = new ArrayList<String> ();
palabras.add ("hola"); palabras.add ("que"); palabras.add ("tal");
Predicado<String> medir3 = new Predicado<String> () {
public boolean esCierto (String e) {
return e.length() == 3;
}
};
Collection<String> res = Buscar.losQueCumplen (palabras, medir3);
Programación G-ISTSI 177

for (String s : res) {


System.out.println (s + " mide 3 ");
}
Programación G-ISTSI 178

7.5
Funciones con cantidad variable de argumentos
 Aunque no se trate de plantillas, sí son en cierto modo una genericidad, ya que permiten,
escribiendo una sola vez un método, que éste puede ser invocado en distintas ocasiones con una
cantidad diferente de argumentos.
 En el siguiente ejemplo vemos cómo se escribe un método que calcula la media de los números
que recibe, pudiendo estos variar en cantidad:
class Main {
public static double media (double ... args) {
double suma = 0;
for (double d : args) {
suma = suma + d;
}
return suma / args.length;
} // ()
public static void main (String[] args) {
double m;
m = media (1.2, 3.4, 4.5);
m = media (1.2, 3.4, 4.5, 6.7, 8.9);
} // ()
} // class
Programación G-ISTSI 179

 Si se necesitara una función que reciba argumentos de distintos tipos, habría que recurrir a
Object ... args y después decidir qué hacer en función del tipo utilizando instanceof . Por
ejemplo:
class Main {
public static void f (Object ... args) {
int total = 0; StringBuffer sb = new StringBuffer();
for (Object o : args) {
if (o instanceof Integer) {
total += (Integer) o;
}
if (o instanceof String) {
sb.append (o + " ");
}
} // for
System.out.println (" suma = " + total);
System.out.println (" frase = " + sb);
} // ()

public static void main (String[] args) {


f (1, "hola", 2, 3, "y", "adios", 4);
} // ()
} // class
180

Jordi Bataller Mascarell 8 septiembre 2014

You might also like