Final Java-Olivia Gallego Toscano
Final Java-Olivia Gallego Toscano
FINAL JAVA
Olivia Gallego Toscano
Tipo Test: …p.1-3
Tema 5:
Ej1: función recursiva suma……………………………………….p.4
Ej2: función recursiva muestra números entre limites…………….p.5
Ej3: función recursiva nº dígitos…………………………………..p.6
Ej4: función recursiva potencias…………………………………..p.7
Ej5: función recursiva Max de una lista……………………………p.8
Ej6: función recursiva cadena dígitos hexadecimal ………………p.9
Ej7: Calcular C (n,k)………………………………………………p.10
Ej8: Crea cadenas ADN……………………………………………p.11-12
Ej9: Ordenar fichero texto alfabéticamente……………………….p.13-14
Ej10: Buscar palabra en fichero de texto …………………………p.15
Ej11: búsqueda binaria fichero de texto …………………………. p.16
Ej12: ordenar fechas….……………………………………………p.17
Tema 6:
Ej1: clase pareja de cualquier tipo………………………………….p.19
Ej2: lista de nº aleatorio……………………………………………..p.20
Ej3: lista de parejas de nº aleatorios ………………………………..p.21
Ej4: lista caracteres, eliminar o introducir caracteres………………..p.22-23
Ej5: lista caracteres, eliminar o añadir en cualquier posición ………p.24-25
Ej6: lista caracteres ordenados, eliminar o añadir caracteres ……….p.26
Ej7: mapa nº y palabra ………………………………………………p.27
Ej8: mapa nº y letra………………………………………………….p.28
Ej9: TreeSet nombres y apellidos …………………………………..p.29-30
Ej10: ventas de una tienda………………………………………… p.31-33
Ej11: leer y ordenar líneas de archivo de texto ………………….. p.34
Ej12: lista nombres todos los archivos ……………………………p.35-36
Ej13: TreeSet ordena 1 millón de enteros…………………………p.37
Preguntas Desarrollo :
Diferencia recursion lineal final y no lineal final ……………………..p.38
Explicación método búsqueda binaria …………………………………p.38
Diferencia pila y cola…………………………………………………..p.39
Diferencia tabla Hash y árbol………………………………………….p.40
Diferencia pila y cola, inserción, acceso aleatorio y búsqueda………..p.41
Diferencia tabla Hash y árbol, inserción, acceso aleatorio, búsqueda y
crecimiento……………………..…………………………………….p.42-43
Limitaciones análisis O………….. …………………………………..p.43
Ejercicios Prácticos:
Función recursiva lista inversa……….……………………………p.44
Función recursiva suma dígitos ……………………………………p.45
Función recursiva x^y………………………………………………p.46
Tipo de recursividad de C(n,k)……………………………………..p.47
Complejidad computacional de funciones………………………….p.47
Calcular tamaño A =200n B = 2n^2………………………………p.48
Calcular tamaño A = 1024n. B = 16n^3…………………………..p.48
Algoritmo Quicksort……………………………………………….p.48
Escribir código de obtenerFechas() con diferentes bucles …………p.49
Complementar clase GotIO………………………………………..p.50-51
Analizar código recursive………………………………………….p.51-52
Analizar UML Polígono, coche y ordenador………………………p.52
Analizar UML Persona, cita trabajador…………………………….p.53
Analizar código Dinosaurio………………………………………..p.54-55
Ejercicios Desarrollo :
Aplicación contactos móvil Android……………………………..p.56-62
Red social Twitter…………………………………………………p.63-67
Clase Utils cargar lista usuarios…………………………………..p.68-71
Juego de Tronos…………………………………………………..p.72-84
Explicaciones telemedicina cantidad memoria (4KB)……………p.85
Clase número complejo …………………………….………………p.86-87
FuncionInterface Newton-Raphson…………………………………p.88-90
Programa que lee otro programa ……………………………………p.91-92
Programa que lee otro programa Ejemplo2…………………………p.93-94
Aplicación contactos móvil Android………………………………..p.95-100
Ejercicios Extra :
Clase estudiantes y orden por edad……….………………………p.101-102
Ventas tienda orden por precio y códigos…………………………p.103-104
Figuras Geométricas………………………………………………p.105-108
Biblioteca y orden alfabéticamente……………………………….p.109-113
Cartas y barajarlas…….…………………………………………..p.114-115
Tienda Online…….……………………………………………….p.116-119
Electrodomésticos………….……………………………………..p.120-124
Serie , Videojuego y Entrega……………………………………..p.125-128
Clase de Alumnos y profesores (materias mates, filo y física)……p.129-133
Baraja cartas española……………………………………………p.134-137
Ruleta rusa………………………………………………………..p.138-140
Almacén de bebidas………………..………………………….…p.141-145
Tipo Test
1. Cuál de las siguientes afirmaciones es falsa:
(a) Un objeto es una instancia concreta de una clase abstracta
(b) Un objeto es una instancia concreta de una clase
5. Si tenemos una variable local, declarada dentro del ámbito de un bloque try podemos
afirmar que:
(a) La variable no es visible dentro de los bloques catch y finally.
(b) La variable es visible dentro del bloque finally pero no dentro del bloque catch.
6. El hecho de que un objeto pueda utilizarse en cualquier método que reciba como
parámetro a cualquier instancia de sus clases padre, se denomina:
(a) Herencia
(b)Polimorfismo
7. Gracias al mecanismo de herencia, una clase hija tiene acceso a públicos, protegidos y
amistosos definidos en su clase padre:
(a) Verdadero.
(b) Falso
8. Al hecho de que, dentro de una misma clase existan dos métodos con el mismo
nombre pero diferente numero/tipo de argumentos, se le denomina:
(a) Sobre escritura de métodos
(b) Sobrecarga de métodos
9. Al utilizar un array en Java podemos acceder a la longitud del mismo gracias a que el
atributo que define dicha longitud tiene una visibilidad:
(a) public
(b) protected
1
11. Cuando una subclase especifica un método que tiene: el mismo nombre, el mismo
numero y el mismo tipo de argumentos que un método declarado en alguna de sus
superclases, esa clase está:
(a) Sobreescribiendo ese método
(b) Sobrecargando ese método
15. Imagine que la clase C hereda de la clase B. Y la clase B hereda de la clase A. Cuando
se cree un objeto de la clase C, ¿Cuál será la secuencia en que se ejecutarán los
constructores?
(a) Primero el constructor C, luego el constructor B y, finalmente, el constructor A.
(b) Primero el constructor A, luego el constructor B y, finalmente, el constructor C.
17. Una clase de Java puede estar formada por clases, métodos y funciones.
(a) Verdadero
(b) Falso.
19. Las variables y objetos en Java deben liberarse de forma explícita cuando no se vayan
a utilizar más:
(a) Verdadero
(b) Falso.
2
21. Los elementos que definen a un objeto son:
a) El tipo de visibilidad que tiene asignado: privado o público.
b) Los atributos que representan su estado y los métodos que representan su comportamiento.
23. Imagine que la clase C hereda de la clase B. Y la clase B hereda de la clase A. Cuando
se cree un objeto de la clase C, ¿cuál será la secuencia en que se ejecutarán los
constructores?
a) Primero el constructor de C, luego constructor de B y, finalmente, el constructor de A
b) Primero el constructor de A, luego constructor de B y, finalmente, el constructor de C
3
EJERCICIOS
TEMA 5
1. Construir una función recursiva que calcule la suma de los n primeros números naturales.
package org.example;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
4
2.Construir una función recursiva que imprima la lista de números naturales comprendidos entre dos
valores a y d dados por el usuario.
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
5
3.Escribir una función recursiva que devuelva la cantidad de dígitos de un número entero.
package org.example;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
6
4. Escribir una función recursiva que calcule xy mediante multiplicaciones sucesivas, siendo x e y dos
números enteros.
package org.example;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
I
5. Calcula mediante un diseño recursivo el valor máximo de un vector de componentes numéricas.
package org.example;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
8
6. Escribir la función recursiva que recibiendo como parámetros una cadena de dígitos
hexadecimales y su longitud devuelva el valor decimal que representa dicha cadena.
public class Ejercicio6 {
public static void main(String[] args)throws IOException {
int tamanio=0;
try{
BufferedReader consola= new BufferedReader(new
InputStreamReader(System.in));
System.out.println("Dame el tamanio del vector: ");
tamanio= Integer.parseInt(consola.readLine());
String []lista= new String[tamanio];
for(int i=0; i<tamanio;i++){
System.out.println("Dame la cadena nº"+i+": ");
lista[i]= consola.readLine();
}
for(int i=0; i<tamanio;i++){
System.out.println(decimales(lista, tamanio));
}
}catch (IOException e){
System.out.println(e.getMessage());
}
}
public static int decimales(String []lista, int tamanio){
int decimal=0;
for(int i=0; i<tamanio; i++){//estudiamos todos los hexadecimales
del array
String hex = lista[i];
int base = 1;//representa la base a la que se está
convirtiendo
// (en este caso, 16 para dígitos hexadecimales)
int dec = 0;//es el valor decimal actual de la cadena.
for (int j = hex.length() - 1; j >= 0; j--) {
char digit = hex.charAt(j);
int val = 0;
if (digit >= '0' && digit <= '9') {
val = digit - '0';
} else if (digit >= 'A' && digit <= 'F') {
val = 10 + digit - 'A';
} else if (digit >= 'a' && digit <= 'f') {
val = 10 + digit - 'a';
}
dec += val * base;
base *= 16;
}
decimal += dec;
}return decimal; } }
9
7. Calcular C (n,k) siendo: C (n,k)= C (n-1,k) + C (n-1,k-1) si n>k>0, 1 en otro caso.
package org.example;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
10
8. Cada tres bases de una cadena de ADN codifican un aminoácido de una proteína. Existe una
secuencia especial de bases de la cadena, ATG, que es una especie de “marca” del principio de un
gen, esto es, una secuencia de bases que codifica una determinada proteína. También existen tres
secuencias diferentes de fin de gen. Podemos a partir de una cadena de ADN contar cuántos genes
tiene contando el número de veces que ocurre la tripleta ATG. Genera una cadena de ADN aleatorio,
y a continuación cuenta cuántos genes contendría si se tratase de una cadena de ADN real.
package org.example;
import java.util.Random;
11
int finGen = encontrarFinGen(adn, index); // Buscar la
secuencia de fin de gen correspondiente
if (finGen == -1) { // Si no se encontró la secuencia de
fin de gen correspondiente
break; // Salir del ciclo while
} else {
index = finGen; // Continuar buscando genes a partir
del fin del gen actual
}
}
}
return numGenes;
}
12
9. Dado un fichero de texto ordena sus líneas alfabéticamente, generando un archivo nuevo que
contenga el texto ordenado.
package org.example;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
13
// Función que escribe las líneas de un ArrayList en un archivo
public static boolean escribirLineasArchivo(String archivo,
ArrayList<String> lineas) {
try (FileWriter fw = new FileWriter(archivo)) {
for (String linea : lineas) {
fw.write(linea + "\n");
}
return true;
} catch (IOException e) {
System.out.println("Error al escribir en el archivo " +
archivo + ".");
e.printStackTrace();
return false;
}
}
}
14
10. Dado un fichero de texto realizar un programa que permita buscar en él una palabra dada. Las
palabras no tienen por qué estar ordenadas alfabéticamente y ninguna de ellas contiene la letra ñ.
package org.example;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
15
11. Modificar el programa anterior para que emplee una búsqueda binaria.
package org.example;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.Arrays;
16
12. Escribir un programa que permita introducir fechas al usuario. El usuario podrá introducir n
fechas, y a continuación podrá listar todas las fechas ordenadas cronológicamente.
package org.example;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Scanner;
17
EJERCICIOS
TEMA 6
1. Define una clase "Pareja", que podrá contener una pareja de datos de cualquier tipo (por ejemplo,
dos enteros, dos reales, dos cadenas de caracteres…). Crea un constructor que permita crear una
instancia de la clase, y proporciona dos métodos para acceder a cada uno de los dos datos de la
pareja de datos.
@Override
public String toString() {
return primero+", "+segundo;
}
}
19
2. Escribe un programa que cree una lista de datos reales. Introduce 100 valores aleatorios en la lista
y muéstralos por pantalla. Emplea al menos dos implementaciones diferentes de la lista.
package org.example;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Random;
28
3.Repite el ejercicio anterior, pero en esta ocasión crea una lista con 100 Parejas (clase del ejercicio
1). Las parejas deberán ser de enteros.
package org.example;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
21
4. Haz un programa que pida cadenas de caracteres al usuario y las vaya introduciendo en una lista.
El usuario tendrá una segunda opción en el menú del programa que consistirá en eliminar el último
dato introducido en la lista. Cada vez que se elimine o se introduzca un dato en la lista, se visualizará
el contenido completo de la lista. ¿Qué tipo de lista has empleado? ¿Por qué?
El motivo de utilizar una lista doblemente enlazada ( LinkedList) es que permite una
inserción y eliminación eficientes en cualquier posición de la lista, sin necesidad de
mover elementos. Además, como la lista puede crecer o decrecer dinámicamente, no es
necesario prever un tamaño máximo para ella. En cambio, si hubiéramos utilizado una
lista con un tamaño fijo (como un array), habría sido más difícil permitir la inserción y
eliminación en cualquier posición sin tener que reordenar los elementos de la lista.
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.LinkedList;
import java.util.List;
posicion=Integer.parseInt(teclado.readLine());
posicion -= 1;
if (posicion >= 0 && posicion <
lista.size()) {
22
lista.remove(posicion);
} else {
System.out.println("Posición inválida");
}
break;
default:
return;
}
}while (opcion!=3);
}catch(IOException e){
System.out.println(e.getMessage());
}
}
public static void menu(){
System.out.println("1. Introducir caracter\n2. Eliminar
caracter\nOPCION:");
}
}
23
5. Haz un programa que pida cadenas de caracteres al usuario y las vaya introduciendo en una lista.
El usuario podrá introducir las cadenas de caracteres en cualquier posición de la lista. Cada vez que
introduzca una nueva cadena de caracteres, indicará la posición donde la quiere introducir y la
cadena de caracteres a introducir. El usuario también podrá eliminar cualquier cadena de caracteres
de la lista. ¿Qué tipo de lista has empleado? ¿Por qué?
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
24
System.out.println(e.getMessage());
}
}
public static void menu(){
System.out.println("1.Introducir cadena\n2.Eliminar
cadena\nOPCION:");
}
}
Se utiliza un ArrayList en el programa debido a su tamaño dinámico, acceso rápido por índice y
eficiencia en la inserción y eliminación de elementos. Además, su capacidad para almacenar
elementos duplicados ofrece flexibilidad en caso de que se requiera.
25
6. Haz un programa que pida cadenas de caracteres al usuario y las vaya almacenando. Los datos
deberán estar siempre ordenados. El usuario deberá tener también una opción para eliminar las
cadenas de caracteres. Cada vez que introduzca o elimine una cadena de caracteres, deberá
realizarse un listado de todos los datos. ¿Qué tipo de estructura de datos has empleado? ¿Por qué?
26
7. Crea un mapa que contenga todos los números entre cero y 10 como valor, siendo la clave de
cada uno de ellos el nombre del número en texto. Lista el contenido del mapa.
import java.util.HashMap;
import java.util.Map;
27
8. Escribe un programa que, empleando un mapa, permita introducir al usuario conjuntos de pares
número/letra. El programa deberá permitir recuperar una letra a partir de su número
correspondiente.
import java.io.BufferedReader; import java.io.IOException;
import java.io.InputStreamReader; import java.util.HashMap; import
java.util.Map;
28
9. Empleando un TreeSet, escribe un programa que lea del usuario pares nombre/apellido de una
persona. El nombre completo (nombre y apellido) de la persona deberá representarse mediante una
clase. Empleando el TreeSet, mostrar ordenados alfabéticamente los nombres por apellido.
//Se utiliza impletents Comparable cuando en la clase
//principal tenemos un TreeSet y asi se puede ordenar
//de forma alfabetica.
public class Persona implements Comparable<Persona>{
private String nombre;
private String apellido;
public Persona(String nombre, String apellido){
this.nombre=nombre;
this.apellido=apellido;
}
public String getNombre() {
return nombre;
}
public void setNombre(String nombre) {
this.nombre = nombre;
}
public String getApellido() {
return apellido;
}
public void setApellido(String apellido) {
this.apellido = apellido;
}
@Override
public String toString() {
return "Nombre: "+nombre+"\nApellido: "+apellido;
}
@Override
public int compareTo(Persona otraPersona) {
// Comparamos por apellido y luego por nombre
int resultado = this.apellido.compareTo(otraPersona.apellido);
if (resultado == 0) {
resultado = this.nombre.compareTo(otraPersona.nombre);
}
return resultado;
}
}
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.TreeSet;
29
String nombre;
String apellido;
int opcion=0;
try{
System.out.println("Introduce pares nombre/apellido (escribe
'fin' para terminar):");
do{
menu();
opcion=Integer.parseInt(teclado.readLine());
switch (opcion){
case 1:
System.out.print("Nombre: ");
nombre = teclado.readLine();
if (nombre.equalsIgnoreCase("fin")) {
break;
}
System.out.print("Apellido: ");
apellido = teclado.readLine();
personas.add(new Persona(nombre, apellido));
break;
case 2:
for (Persona persona : personas) {
System.out.println(persona);
}
break;
default:
return;
}
}while (opcion!=3);
30
10. Crea un programa que lea registros de tipo "venta" de la entrada estándar. Una venta constará,
al menos, del nombre del producto vendido, el nombre del cliente que lo ha adquirido, el precio de
la transacción y la fecha en la que se realizó la transacción. El programa deberá permitir listar los
registros introducidos ordenándolos según la fecha en la que se realizó la venta, alfabéticamente por
el nombre del cliente y según el precio de la transacción.
import java.time.LocalDate;
31
}
@Override
public String toString() {
return "Nombre Producto: "+nomProducto+
"\nNombre Comprador: "+nomComprador+
"\nCoste venta: "+coste_venta+
"\nCoste compra: "+coste+
"\nFecha: "+fecha+"\n";
}
}
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;
import java.util.Comparator;
32
System.out.println("Dia: ");
dia=Integer.parseInt(teclado.readLine());
System.out.println("Mes: ");
mes=Integer.parseInt(teclado.readLine());
System.out.println("Anio: ");
anio=Integer.parseInt(teclado.readLine());
fecha= LocalDate.of(anio,mes,dia);
Venta venta= new Venta(coste,
nomProducto,nomComprador,coste_venta,fecha);
ventas.add(venta);
break;
case 2:
System.out.println("\nVentas registradas:");
ventas.sort(Comparator.comparing(Venta::getFecha)
.thenComparing(Venta::getNomComprador)
.thenComparingInt(Venta::getCoste_venta));
for (Venta venta1: ventas) {
System.out.println(venta1);
}
break;
default:
return;
}
}while (opcion!=3);
}catch (IOException e){
System.out.println(e.getMessage());
}
}
public static void menu(){
System.out.println("1.Registrar venta\n2.Mostrar ventas\nOPCION:
");
}
}
33
11. Escribe un programa que lea todas las líneas de texto de un archivo de texto, a continuación, las
ordene, y escriba un nuevo archivo con el contenido ordenado.
import java.io.*; import java.util.ArrayList; import
java.util.Collections; import java.util.List;
try {
// Leer el archivo de entrada
List<String> lines = leerArchivo(inputFile);
34
12. Escribe un programa que liste el nombre de todos los archivos de tu disco duro de modo
recursivo y vaya guardando el nombre en memoria, y que asocie este nombre a su ruta completa.
Una vez haya terminado el programa, el usuario podrá introducir el nombre de un archivo, y el
programa le mostrará su ruta completa. El programa también deberá permitir ordenar
alfabéticamente los nombres de todos los archivos y listarlos junto con la ruta completa en la que se
encuentran. (Para que el programa termine en un tiempo razonable, puedes sólo indexar el
contenido de un directorio de tu ordenador y no de todo tu disco duro).
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.*;
35
System.out.println("El archivo '" + nombreBuscado + "' no
fue encontrado.");
}
} catch (IOException e) {
System.out.println(e.getMessage());
}
}
if (archivosEnDirectorio != null) {
for (File archivo : archivosEnDirectorio) {
if (archivo.isFile()) {
// Guardar el nombre del archivo y su ruta completa en
memoria
archivos.put(archivo.getName(),
archivo.getAbsolutePath());
} else if (archivo.isDirectory()) {
// Realizar una búsqueda recursiva en los
subdirectorios
buscarArchivos(archivo.getAbsolutePath());
}
}
}
}
}
36
13. Compara el rendimiento de ordenar 1 millón de enteros aleatorios empleando un TreeSet,
colocándolos en una lista y empleando el método de ordenar de la clase Collections, y colocándolos
en un array y empleando el método de ordenar de la clase Arrays
import java.util.*;
37
PREGUNTAS
DESARROLLO
Explica las diferencias entre la recursión lineal final y la recursión lineal no final y nombra un ejemplo
de cada una.
La recursión lineal no final se caracteriza por combinar el resultado de la llamada recursiva en una
expresión para obtener el resultado de la función que la llama. Puede haber operaciones adicionales
después de la llamada recursiva antes de retornar un valor o finalizar la función. Un ejemplo típico
de este tipo de recursión es el cálculo del factorial de un número, donde el factorial de un número n
se obtiene multiplicando n por el factorial de n-1. En este caso, la llamada recursiva ocurre antes de
la operación de multiplicación.
Por otro lado, en la recursión lineal final, el resultado devuelto por la función es directamente el
resultado de la última llamada recursiva. En este caso, la llamada recursiva ocurre al final de la
función después de realizar todas las operaciones necesarias. Un ejemplo de esto es el cálculo del
factorial de un número, donde la llamada recursiva es la última instrucción ejecutada antes de
retornar el resultado.
La búsqueda binaria es un algoritmo eficiente utilizado para buscar un elemento específico en una
lista ordenada. Funciona dividiendo repetidamente la lista en dos mitades y descartando una de ellas
en función de si el elemento buscado es mayor o menor que el elemento en el medio.
Este proceso de división y descarte continúa hasta que se encuentre el elemento deseado o se
determine que no está presente en la lista.
Tiene los siguientes prerrequisitos:
- La lista debe estar ordenada: La búsqueda binaria solo funciona correctamente en una lista que
esté ordenada de forma ascendente o descendente. Si la lista no está ordenada, el algoritmo no
podrá encontrar el elemento deseado de manera eficiente.
- Acceso aleatorio a los elementos: Para dividir la lista y buscar en las mitades, se necesita un acceso
aleatorio a los elementos. Esto significa que se debe poder acceder directamente a cualquier
elemento de la lista en función de su posición, como en un arreglo o una estructura de datos que
admita el acceso aleatorio, como un vector. Si los prerrequisitos anteriores se cumplen, la búsqueda
binaria puede realizar búsquedas eficientes en una lista ordenada, reduciendo el espacio de
búsqueda a la mitad en cada paso.
38
Explica las diferencias entre una pila y una cola. ¿Cuándo conviene utilizar cada una de estas
estructuras? ¿Cuál es el orden de complejidad computacional de estas estructuras? No redacte el
orden tal y como puede ver en los apuntes, razónelo con sus propias palabras y argumentos.
Una lista es una estructura dinámica de datos constituida por nodos, donde se puede acceder,
eliminar y modificar elementos en cualquier posición. Tiene dos posibles tipos, pila y cola.
La Pila es una estructura de datos LIFO, que quiere decir que el ultimo que el último elemento
insertado es el primero en ser eliminado de la lista. Sus operaciones principales son apilar (push)
para agregar un elemento en la parte superior de la pila, desapilar (pop) para eliminar el elemento
superior de la pila, obtener el elemento del principio de la fila sin sacarlo de ella (top), y comprobar
si está vacía o vaciar la pila. Esta estructura se utiliza cuando se requiere un acceso rápido a los
elementos más recientes y no se necesita acceso directo a elementos intermedios o antiguos. Es útil
en computación para ejecutar programas por los compiladores o intérpretes, por ejemplo.
La Cola tiene una estructura FIFO, que significa, que el primer elemento que se agrega es el primero
es ser eliminado. Sus operaciones principales son añadir o eliminar un elemento, comprobar si la
cola está vacía o vaciarla, y obtener el primer elemento de la cola. Se utiliza cuando se necesita
mantener el orden original de los elementos y se requiere acceso rápido al elemento más antiguo. Es
útil en situaciones como la gestión de tareas en un sistema operativo (planificación de procesos), la
implementación de buffers o la implementación de algoritmos como el recorrido en amplitud de un
grafo.
Las operaciones principales (apilar, desapilar, encolar, desencolar) tienen un orden de complejidad
O(1), es decir, son operaciones de tiempo constante. Esto se debe a que el acceso y modificación de
los elementos se realiza directamente en el extremo correspondiente de la estructura (parte
superior de la pila o principio de la cola).
39
Explica las diferencias entre una tabla Hash y un árbol. ¿Cuándo conviene utilizar cada una de estas
estructuras? Justifique su respuesta y analice todas las posibles operaciones a realizar por las
estructuras. No redacte el orden tal y como puede ver en los apuntes, razónelo con sus propias
palabras y argumentos.
Las tablas hash y los árboles son estructuras de datos con características diferentes y se utilizan en
diferentes situaciones.
Las tablas hash son adecuadas cuando se necesita un acceso rápido a los datos y las colisiones son
mínimas. Son eficientes para buscar elementos por su clave única y también para insertar y eliminar
elementos cuando no hay muchas colisiones. Sin embargo, cuando el número de elementos es alto
en comparación con el tamaño del array base de la tabla, es probable que haya muchas colisiones, lo
que puede degradar el rendimiento. En general, las tablas hash son útiles cuando se necesita un
acceso rápido a los datos y las colisiones son gestionables.
Por otro lado, los árboles son una buena alternativa cuando se requiere encontrar, insertar o
eliminar elementos de manera eficiente sin restricciones en el número de elementos. Los árboles
son estructuras jerárquicas en las que cada nodo tiene un único antecesor y varios sucesores. Los
árboles binarios, en particular, son útiles para almacenar elementos con una relación de orden
definida de manera eficiente. La búsqueda en un árbol binario tiene un tiempo de O(log2(n)), lo que
lo hace bastante eficiente incluso para conjuntos de datos grandes. Los árboles también son
estructuras dinámicas, lo que significa que se pueden insertar y eliminar nodos en cualquier parte
del árbol sin necesidad de reorganizar la estructura completa.
En resumen, las tablas hash son adecuadas cuando se necesita un acceso rápido a los datos y las
colisiones son mínimas, mientras que los árboles son útiles cuando se requiere una búsqueda
eficiente y una estructura dinámica que permita insertar y eliminar elementos en cualquier posición.
La elección entre estas estructuras dependerá de las necesidades específicas del problema y de la
relación entre el número de elementos y el tamaño esperado del conjunto de datos.
40
Explica las diferencias entre una pila y una cola. ¿cuando conviene utilizar cada una de estas
estructuras ¿cual es el orden de complejidad de una operación de (1) insercion, de (2) acceso
aleatoro, y de (3) búsqueda de un dato?
Una pila y una cola son estructuras de datos lineales que se utilizan para organizar y manipular
conjuntos de elementos. La principal diferencia entre una pila y una cola radica en el orden en el que
se añaden y se eliminan los elementos.
Una pila (stack) sigue el principio de "último en entrar, primero en salir" (LIFO, Last-In-First-Out).
Esto significa que el último elemento que se añade a la pila es el primero en ser eliminado. Los
elementos se añaden y se eliminan solo en un extremo de la pila, conocido como la cima (top) de la
pila.
Una cola (queue), por otro lado, sigue el principio de "primero en entrar, primero en salir" (FIFO,
First-In-First-Out). Esto significa que el primer elemento que se añade a la cola es el primero en ser
eliminado.
- Inserción:
o Pila: La inserción en una pila (push) tiene un tiempo de complejidad O(1), ya que
simplemente se añade un elemento en la cima de la pila.
o Cola: La inserción en una cola también tiene un tiempo de complejidad O(1), ya que
se añade un elemento en la parte trasera de la cola.
- Acceso aleatorio:
o Pila: No hay acceso aleatorio en una pila. Solo se puede acceder al elemento que se
encuentra en la cima de la pila (top), y tiene un tiempo de complejidad O(1).
o Cola: Al igual que en una pila, no hay acceso aleatorio en una cola. Se puede acceder
al elemento que se encuentra en el frente o en la parte trasera de la cola, ambos con
un tiempo de complejidad O(1).
- Búsqueda de un dato:
o Pila: Las pilas no son eficientes para la búsqueda de un dato en particular, ya que no
proporcionan acceso aleatorio a sus elementos. Se tendría que realizar una
eliminación secuencial de elementos hasta encontrar el dato deseado, lo cual
tendría un tiempo de complejidad O(n), donde n es el número de elementos en la
pila.
o Cola: Al igual que en una pila, las colas tampoco son eficientes para la búsqueda de
un dato en particular. Se tendría que realizar una eliminación secuencial de
elementos hasta encontrar el dato deseado, lo cual tendría un tiempo de
complejidad O(n), donde n es el número de elementos en la cola.
41
Explica las diferencias entre una tabla Hash y un árbol. ¿Cuando conviene utilizar cada una de estas
estructuras? ¿cual es el orden de complejidad de una operacion de (1) insercion, de (2) acceso
aleatorio, de (3) búsqueda de un dato, y de (4) crecimiento de la estructura?
Las tablas hash y los árboles son estructuras de datos con características diferentes y se utilizan en
diferentes situaciones.
Las tablas hash son adecuadas cuando se necesita un acceso rápido a los datos y las colisiones son
mínimas. Son eficientes para buscar elementos por su clave única y también para insertar y eliminar
elementos cuando no hay muchas colisiones. Sin embargo, cuando el número de elementos es alto
en comparación con el tamaño del array base de la tabla, es probable que haya muchas colisiones, lo
que puede degradar el rendimiento. En general, las tablas hash son útiles cuando se necesita un
acceso rápido a los datos y las colisiones son gestionables.
Por otro lado, los árboles son una buena alternativa cuando se requiere encontrar, insertar o
eliminar elementos de manera eficiente sin restricciones en el número de elementos. Los árboles
son estructuras jerárquicas en las que cada nodo tiene un único antecesor y varios sucesores. Los
árboles binarios, en particular, son útiles para almacenar elementos con una relación de orden
definida de manera eficiente. La búsqueda en un árbol binario tiene un tiempo de O(log2(n)), lo que
lo hace bastante eficiente incluso para conjuntos de datos grandes. Los árboles también son
estructuras dinámicas, lo que significa que se pueden insertar y eliminar nodos en cualquier parte
del árbol sin necesidad de reorganizar la estructura completa.
Inserción:
- Tabla Hash: En el caso promedio, la inserción en una tabla hash tiene una complejidad de
O(1), es decir, constante. Sin embargo, en el peor caso, cuando hay muchas colisiones, la
complejidad puede llegar a ser O(n), donde n es el número de elementos.
- Árbol: La inserción en un árbol balanceado, tiene una complejidad de O(log n), donde n es el
número de elementos.
Acceso aleatorio:
- Tabla Hash: En el caso promedio, el acceso aleatorio en una tabla hash tiene una
complejidad de O(1), es decir, constante. Sin embargo, en el peor caso, cuando hay
colisiones, la complejidad puede ser O(n), donde n es el número de elementos.
- Árbol: El acceso aleatorio en un árbol balanceado tiene una complejidad de O(log n), donde
n es el número de elementos.
Búsqueda de un dato:
- Tabla Hash: En el caso promedio, la búsqueda en una tabla hash tiene una complejidad de
O(1), es decir, constante. Sin embargo, en el peor caso, cuando hay colisiones, la
complejidad puede ser O(n), donde n es el número de elementos.
- Árbol: La búsqueda en un árbol balanceado tiene una complejidad de O(log n), donde n es el
número de elementos.
Crecimiento de la estructura:
42
- Tabla Hash: El crecimiento de una tabla hash implica redimensionar la tabla, lo que puede
tener una complejidad de O(n), donde n es el número de elementos. Esto se debe a que se
deben recalcular las funciones de hash y reorganizar los elementos en la nueva tabla.
- Árbol: Los árboles pueden crecer dinámicamente sin necesidad de redimensionamiento,
pero algunos tipos de árboles balanceados pueden requerir operaciones de reequilibrio,
como rotaciones, con una complejidad de O(log n), donde n es el número de elementos.
¿Cuáles son las limitaciones del análisis O para medir la complejidad Computacional?
En conclusión, el análisis "O" es una herramienta valiosa para evaluar la complejidad computacional,
pero tiene limitaciones en términos de tamaños de entrada pequeños, constantes ocultas, recursos
limitados y consideraciones específicas de las estructuras de datos. Es importante complementar el
análisis "O" con pruebas empíricas y considerar estos factores adicionales para comprender
completamente el rendimiento de un algoritmo en la práctica.
43
EJERCICIOS
PRÁCTICOS
Una lista reversa consiste en la ordenación de los elementos de la forma inversa. Diseña e
implementa el pseudocódigo de una función recursiva que devuelva una nueva lista inversa a la lista
introducida como parámetro S(n) u ordene la lista recibida como parámetro de forma inversa S(1).
La implementación del algoritmo con una nueva lista S(n) en lugar de la ordenación en la propia lista
S(1) será puntuable hasta 1 punto.
44
Calcular de forma recursiva la suma de los dígitos de un número siendo un ejemplo el número 102 ->
1 + 0 + 2 = 3. Recuerde que puede realizar los cambios de tipo que crea necesarios para facilitar la
resolución:
¿Qué tipo de recursividad se está empleando? ¿Qué complejidad computacional tiene el algoritmo?
De lo contrario, se obtiene el último dígito del número tomando el módulo 10 (numero % 10). Luego,
se divide el número entre 10 para eliminar el último dígito (numero / 10) y se llama recursivamente
a la función sumaDigitos con el número restante.
La llamada recursiva se repite hasta que el número sea menor que 10 y, en cada llamada, se suman
los últimos dígitos obtenidos.
45
a) Escribir una función recursiva que calcule xy mediante multiplicaciones sucesivas, siendo x e y
b) Escribir una función recursiva que calcule xy con un orden computacional de O log(n)
aprovechando la propiedad
Xn*m = (Xn)m
Por ejemplo:
a)
b)
46
Calcular de forma recursiva C (n,k) siendo:
La función se define en función de sí misma y utiliza una recursividad múltiple, ya que hay dos
llamadas recursivas dentro del cuerpo de la función.
La recursión múltiple se produce porque se realiza una llamada recursiva para calcular C(n-1, k) y
otra llamada recursiva para calcular C(n-1, k-1). Estas dos llamadas recursivas se combinan mediante
la suma para obtener el resultado final.
Calcula la complejidad computacional de las siguientes funciones y ordénalas según orden creciente:
a) 4n * log(n) + 2n
El término dominante es 4n * log(n) ya que crece más rápido que 2n. Complejidad: O(n * log(n))
b) 24
c) 2n
d) 3n + 100 log(n)
El término dominante es 100 log(n) ya que crece más rápido que 3n. Complejidad: O(log(n))
e) n3 + n2 + 10
El término dominante es n3 ya que crece más rápido que n2 y 10. Complejidad: O(n^3)
f) log(n)
47
Dadas dos funciones y su número de operaciones:
A =200n
B = 2n2
Para determinar a partir de qué tamaño de entrada la función A es más eficiente que la función B,
necesitamos encontrar el punto en el cual el número de operaciones de A es menor que el número
de operaciones de B. Dado que A = 200n y B = 2n 2, podemos establecer la siguiente desigualdad:
200n < 2n2
200 < 2n
100 < n
Por lo tanto, para valores de n mayores a 100, la función A (200n) será más eficiente que la función B
(2n2).
A = 1024n
B = 16n3
Para determinar a partir de qué tamaño de entrada la función A es más eficiente que la función B,
necesitamos encontrar el punto en el cual el número de operaciones de A es menor que el número
de operaciones de B. Dado que A = 1024n y B = 16n3, podemos establecer la siguiente desigualdad:
1024n < 16n3
64 < n2
Tomamos la raíz cuadrada de ambos lados de la desigualdad: 8 < n Por lo tanto, para valores de n
mayores a 8, la función A (1024n) será más eficiente que la función B (16n3).
Dibuja las diferencias iteraciones del algoritmo de Quicksort con un ejemplo práctico donde se
elegirá como pivote el primer elemento de la lista para el siguiente conjunto de entrada.
50 20 84 13 22 16 89 8
48
Dado el siguiente código, suponiendo que todas las clases implicadas están correctamente
implementadas y que el método obtenerFechas () devuelve un array de tipo LocalDate de cualquier
longitud, implemente (sin retocar mas
líneas de las indicadas):
(a) Un código equivalente al de las líneas 13-15, utilizando un bucle for clásico.
public static void main(String[] args) {
LocalDate[] fechas = obtenerFechas();
for (int i = 0; i < fechas.length; i++) {
LocalDate fecha = fechas[i];
System.out.println("Fecha: " + fecha);
}
}
49
Entre las clases que implementan el juego : The Iron Throne®,GAME OF THRONES , se encuentra una
denominada DataStore. Esta clase es la responsable de mantener todos los datos de la aplicación
(jugadores, ciudades, armas, etcétera) en memoria principal. En el juego, sólo existe una instancia de
un objeto de tipo DataStore que se carga (y almacena) automáticamente en disco, de manera
binaria, cada vez que la aplicación arranca (y es cerrada).
(a) Complete la clase GotIO implementando un método que recupere un objeto de tipo DataStore
(que ha sido previamente almacenado en el disco duro mediante el método saveDataStore). La
signatura del método que debe escribir es:
(b) Tenga en cuenta que el código mostrado para saveDataStore está incompleto. En el código de su
método loadDataStore deberá cerrar los recursos abiertos y capturar todas las excepciones que
ocurran durante el proceso.
a)
try {
fileInputStream = new FileInputStream(file);
objectInputStream = new ObjectInputStream(fileInputStream);
data = (DataStore) objectInputStream.readObject();
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace(); // Manejo básico de excepciones, puedes
ajustarlo según tus necesidades
} finally {
try {
if (objectInputStream != null) {
objectInputStream.close();
}
if (fileInputStream != null) {
fileInputStream.close();
50
}
} catch (IOException e) {
e.printStackTrace();
}
}
return data;
}
b) En el código anterior se ha tenido en cuenta el cierre de los recursos abiertos en el bloque finally.
Además, se ha agregado la captura de excepciones para manejar cualquier error que pueda ocurrir
durante el proceso de lectura y deserialización del archivo.
Preguntas:
a) ¿Qué imprime el código? En caso de que no cumplile di que el motivo y arregle el programa
b) Explica brevemente qué cálculo está haciendo y qué tipo de recursividad está empleando.
Respuestas:
a)
public class Main {
public static void main(String[] args) {
int a=recursive (1,2);
System.out.println(a);
}
public static int recursive(int a, int b){
if (b==0){
return 1;
} else if (a==0) {
return 0;
}else {
return a*recursive(a,b-1);
St
}
}
}
El código imprime el valor resultante de la llamada al método recursive (1, -2). Sin embargo, esta
llamada causará una excepción debido a que la variable b está decrementando en cada llamada
recursiva sin llegar a alcanzar el valor de 0. Además, el valor de b inicialmente es -2, lo que no
permitirá que la condición b == 0 se cumpla. Para corregir esto, se puede ajustar los valores iniciales
de a y b en la llamada recursive (1, -2).
b) El cálculo que está haciendo el método recursive() es una exponenciación de “a” a la potencia b.
El método utiliza una recursividad descendente, donde en cada llamada se multiplica el valor actual
de a por el resultado de la llamada recursiva con b decrementado en 1. En caso de que b sea 0, se
devuelve 1. Si a es 0, se devuelve 0.
Explica el significado del siguiente diagrama UML. Céntrate especialmente en el significado de las
relaciones entre las clases.
Este diagrama UML nos muestra que los polígonos puesden estar compuestos por círculos,
rectángulos y triángulos. Y este último puede ser de tres tipos: escaleno, isoceles, equilatero.
¿Qué relación existe entre cada par de clases que se muestran bajo estas líneas? Explica la diferencia
entre ambas relaciones.
El primer diagrama nos muestra que los coches pueden tener un motor y cuatro ruedas. Pero al
estar la flecha en blanco nos indica que si no tienen alguna de estas características, igualmente,
seguirá siendo un coche.
En cambio, en el diagrama de la clase ordenador vemos que tiene que estar imperativamente
compuesto por una memoria y un procesador por que si no el dispositivo deja de tener sentido.
52
A partir del diagrama UML que se le muestra a continuación, y centrándose en el significado de los
símbolos UML que se utilizan para representar elementos software, responda a las siguientes
cuestiones:
(a) Describa detalladamente el tipo de relación existente entre las clases Persona, Médico y Técnico.
(b) Describa detalladamente el tipo de relación existente entre las clases Persona y Cita. Indique qué
significan las cardinalidades asociadas a la relación.
a) Una persona es un trabajador: médico y/o técnico. Siendo esto una herencia múltiple.
b) Una persona puede tener 0 a infinitas citas. Esta herencia es una herencia simple y la cardinalidad
indica la cantidad de citas que puede tener una persona.
c) Como el enlace entre la persona y el DNI no es una flecha, sabemos que estas clases tienen una
relación bidireccional, lo que significa que hay una relación entre ellas pero no no específica la
naturaleza exacta.
La asociación entre "Persona" y "DNI" podría representar que una persona tiene un número de
identificación único asociado a ella, lo cual es comúnmente representado por un DNI (Documento
Nacional de Identidad).
53
Teniendo en cuenta las clases definidas en este ejercicio, explique y justifique (basándose en
conceptos de programación orientada a objetos como: herencia, polimorfismo, sobrescritura, . . . ),
las siguientes cuestiones:
Recuerde que debe explicar y justificar, utilizando los conceptos aprendidos, sus respues tas.
Respuestas no justificadas no serán puntuadas.
(a) ¿Qué sucede en las líneas 25-26 de la clase Test? ¿Esa parte del código compila?¿Por qué?
Si compila, ¿qué salida se imprimirá en pantalla al ejecutar esas líneas? ¿Por qué?.
En las líneas 25-26 de la clase Test, se crea un objeto de la clase Dinosaur utilizando el constructor
sin parámetros y luego se llama al método print() de ese objeto. Esta parte del código compila sin
problemas, ya que la clase Dinosaur tiene definido el método print(). La salida impresa en pantalla
será: “I’m a dinosaur.”
Esto se debe a que el objeto creado es de tipo Dinosaur y al llamar al método print(), se ejecuta la
implementación de la clase Dinosaur, que imprime "* I'm a dinosaur.".
(b) ¿Qué sucede en las líneas 27-28 de la clase Test? ¿Esa parte del código compila? ¿Por qué?
Si compila, ¿qué salida se imprimirá en pantalla al ejecutar esas líneas? ¿Por qué?.
En las líneas 27-28 de la clase Test, se crea un objeto de la clase CarnivorousDinosaur utilizando el
constructor sin parámetros y se asigna a la referencia dinosaur. Esta parte del código compila sin
54
problemas, ya que la clase CarnivorousDinosaur es una subclase de Dinosaur y puede ser asignada a
una referencia de tipo Dinosaur. La salida impresa en pantalla será: “* I'm a dinosaur.” “ I'm a
carnivorous dinosaur.” Esto se debe a que al llamar al método print() en la línea 28, se ejecuta la
implementación del método en la clase CarnivorousDinosaur, que a su vez llama al método print() de
la superclase Dinosaur usando super.print(), y luego imprime "* I'm a carnivorous dinosaur.".
(c) ¿Qué sucede en las líneas 29-30 de la clase Test? ¿Esa parte del código compila? ¿Por qué?
Si compila, ¿qué salida se imprimirá en pantalla al ejecutar esas líneas? ¿Por qué?.
En las líneas 29-30 de la clase Test, se crea un objeto de la clase TyrannosaurusRex utilizando el
constructor sin parámetros y se asigna a la referencia dinosaur. Esta parte del código compila sin
problemas, ya que la clase TyrannosaurusRex es una subclase de CarnivorousDinosaur, que a su vez
es una subclase de Dinosaur. Por lo tanto, un objeto de tipo TyrannosaurusRex también se puede
asignar a una referencia de tipo Dinosaur. La salida impresa en pantalla será: “I'm a dinosaur.” “ I'm
a carnivorous dinosaur.” “I'm a TyrannosaurusRex.”
(d)¿Qué sucede en las líneas 31-32 de la clase Test? ¿Esa parte del código compila? ¿Por qué?
Si compila, ¿qué salida se imprimirá en pantalla al ejecutar esas líneas? ¿Por qué?.
En las líneas 31-32 de la clase Test, se crea un objeto de la clase Spinosaurus utilizando el
constructor sin parámetros y se asigna a la referencia dinosaur. Esta parte del código compila sin
problemas, ya que la clase Spinosaurus es una subclase de CarnivorousDinosaur, que a su vez es una
subclase de Dinosaur. Por lo tanto, un objeto de tipo Spinosaurus también se puede asignar a una
referencia de tipo Dinosaur. La salida impresa en pantalla será: “* I'm a dinosaur.” “I'm a
carnivorous dinosaur.” “ I'm a Spinosaurus with 0 years.”
(e) ¿Qué sucede en la línea 33 de la clase Test? ¿Esa parte del código compila? ¿Por qué? Si compila,
¿qué salida se imprimirá en pantalla al ejecutar esa línea? ¿Por qué?.
En la línea 33 de la clase Test, se intenta acceder al atributo years de la referencia dinosaur. Sin
embargo, esto dará un error de compilación porque el atributo years es privado en la clase
Spinosaurus y no es accesible desde fuera de la clase.
(f) ¿Qué sucede en las líneas 34-40 de la clase Test? ¿ Esa parte del código compila? ¿Por qué?
Si compila, ¿qué salida se imprimirá en pantalla al ejecutar estas líneas? ¿Por qué?.
En las líneas 34-40 de la clase Test, se crea un array de objetos de tipo Dinosaur llamado dinosaurs.
Se inicializan dos elementos del array con objetos de las clases TyrannosaurusRex y Spinosaurus,
respectivamente. Luego se llama al método setYears() en el objeto de la clase Spinosaurus para
establecer el valor de la variable years en 5. A continuación, se realiza un bucle for-each para iterar
sobre los elementos del array y llamar al método print() en cada uno de ellos. Esta parte del código
compila sin problemas, ya que las clases TyrannosaurusRex y Spinosaurus son subclases de Dinosaur
y, por lo tanto, se pueden asignar a elementos del array de tipo Dinosaur. La salida impresa en
pantalla será: “ I'm a dinosaur.” “I'm a carnivorous dinosaur.” “I'm a TyrannosaurusRex.” “I'm a
dinosaur.” “I'm a carnivorous dinosaur.” “I'm a Spinosaurus with 5 years.”
55
EJERCICIOS
DESARROLLO
1) En la aplicación de contactos de un móvil con sistema operativo Android, el usuario tiene una
Agenda que incluye una lista de contactos. Cada Contacto almacena, entre otras características, el
nombre, el apodo, una lista de direcciones de email, una lista de Teléfonos y un valor lógico
(true/false) que indica si las llamadas de este contacto hay que silenciarlas.
Por su parte, cada Teléfono incluye su tipología (casa, trabajo, móvil, otros), el número de teléfono al
que hace referencia y un valor lógico que indica si es el teléfono preferido para llamar a esa persona.
56
package org.example.aplicacionContactos;
import java.util.List;
57
}
package org.example.aplicacionContactos;
import java.util.ArrayList;
import java.util.List;
package org.example.aplicacionContactos;
import java.util.ArrayList;
import java.util.List;
public Agenda() {
58
this.contactos = new ArrayList<>();
}
o Incluyen un atributo acerca del tipo de relación que les une: hermanos, padres, suegros, cuñados.
a) Modifique la clase Contacto para incluir el tipo de contacto (familia, amigos, trabajo) e
implemente las clases necesarias para representar los distintos tipos de contactos.
b) Implemente los constructores de las clases reutilizando al máximo todo el código disponible.
Tenga en cuenta que:
- Deberá lanzar una excepción si en el constructor de un contacto de tipo amigo o familia recibe una
fecha de cumpleaños que es mayor que la fecha actual. Se puede suponer implementada el método
de comparación de fecha (fecha1 >/==/< fecha2)
c) Implemente el método __str__(self) de tal manera que se muestren todas las características de
cada contacto. Este método deberá estar disponible para todos los tipos de contactos. Suponga que
el método __str__(self) de la clase Contacto está implementado correctamente y puede instanciarse
mediante la sintaxis super().__str__(). Reutilice al máximo todo el código disponible.
59
d) En la clase Contacto, implemente un método que pueda recibir cualquier tipo de contacto de los
definidos anteriormente (se deben recordar las características de Python y el tipado..). Este método
invocará al __str__() y posteriormente, lo sacará por pantalla. Reutilice todo el código posible.
Explique cómo ha implementado el método y de qué características de Python y de la programación
orientada a objetos se ha beneficiado para realizar esta tarea.
@Override
public String toString() {
return "Nombre: " + nombre + "\n" +
"Apodo: " + apodo + "\n" +
"Dirección de casa: " + direcciónCasa;
}
@Override
public String toString() {
return super.toString() + "\n" +
"Dirección de trabajo: " + direccionTrabajo;
}
60
}
import java.time.LocalDate;
61
}
@Override
public String toString() {
return super.toString() + "\n" +
"Fecha de cumpleaños: " + fechaCumpleaños;
}
}
62
1) En la red social Twitter, cada usuario es propietario de una cuenta (UserAccount) en la que,
básicamente, se especifica un alias (que cumple las funciones de identificador único) y un email de
contacto. En la cuenta, además, se incluye el conjunto de tweets que el propietario va publicando a
lo largo del tiempo.
Como la cantidad de mensajes que maneja la red es inmensa, una característica original de Twitter
es que cada usuario puede seleccionar la información que le interesa recibir. De esta manera, el
propietario de una UserAccount puede convertirse en seguidor (follower) de otros usuarios,
mostrando su interés en los tweets que ellos publiquen. Así, cada vez que un usuario publica un
tweet, éste es incluido en el timeline de la UserAccount de cada uno de sus followers (es decir, el
timeline se corresponde con el conjunto de tweets recibidos).
a) Programe la clase UserAccount y su constructor. Incluya todos sus atributos (alias, email, tweets,
followers, timeline) y establezca la visibilidad adecuada. Indica el tipo de datos de todos los atributos
y parámetros del constructor y suponga que ya tiene implementadas correctamente las clases Tweet
y Email.
Justifique, brevemente, porqué ha seleccionado cada estructura de datos para los atributos. No es
necesario realizar control de excepciones ni pruebas.
• def follow(user2)
• Añada, si lo necesita, métodos auxiliares (por ejemplo, para manejar los followers de
user2).
• def tweet(tweet1)
• Añada, si lo necesita, métodos auxiliares (por ejemplo, para manejar el timeline de los followers).
import java.util.ArrayList;
import java.util.List;
63
public UserAccount(String alias, String email) {
this.alias = alias;
this.email = email;
this.tweets = new ArrayList<>();
this.followers = new ArrayList<>();
this.timeline = new ArrayList<>();
}
El atributo email también se utiliza como tipo String, ya que representa la dirección de correo
electrónico del usuario y se almacena como texto.
El atributo tweets se implementa como una lista (List) de objetos Tweet, lo que permite almacenar
múltiples tweets en el orden en que se publicaron.
El atributo followers es una lista de objetos UserAccount, que representa a los seguidores de la
cuenta de usuario. Almacenar los seguidores en una lista permite un fácil acceso y manejo de los
seguidores.
El atributo timeline también se implementa como una lista de objetos Tweet, ya que representa los
tweets que el usuario ha recibido de sus seguidores. Almacenarlos en una lista permite mantener el
orden cronológico de los tweets en el timeline del usuario.
El método follow() permite a un usuario seguir a otro. Al llamar a este método, el usuario actual
(user) se agrega a la lista de seguidores del usuario al que desea seguir (user2). Esto se logra
mediante el método addFollower() que agrega el seguidor a la lista de seguidores del usuario2.
64
El método tweet() permite a un usuario publicar un tweet. Al llamar a este método, el tweet se
agrega a la lista de tweets del usuario (tweets). Luego, se llama al método
updateFollowersTimeline() para actualizar el timeline de todos los seguidores del usuario con el
nuevo tweet. Este método itera sobre la lista de seguidores y llama al método addToTimeline() de
cada seguidor para agregar el tweet al timeline de cada uno.
2) En la red social Twitter, la unidad básica de información se denomina Tweet. Un Tweet es creado
en un instante de tiempo concreto (time), contiene un mensaje (message) con un máximo de 140
caracteres de longitud y es publicado por un
usuario (conocido como sender). Además, existen dos tipos de Tweet especiales:
• DirectMessage: Los mensajes directos son Tweets que permiten comunicarse, de manera privada,
a dos usuarios dentro de la red. Estos DirectMessage son como Tweets ya que contienen un mensaje
(message), son publicados por un emisor (sender) y son creados en un instante de tiempo
determinado (time); la única diferencia es que incluyen a otro usuario como receptor (receiver) del
tweet.
• Retweet: Cuando un usuario lee un tweet interesante que le ha llegado a su timeline, y quiere
reenviarlo a su lista de followers, crea un retweet. Este Retweet es como un Tweet, es decir, el
usuario que lo publica (sender) puede poner un mensaje (message) y lo crea en un tiempo
determinado (time); la única diferencia es que el Retweet incluye una referencia al Tweet que se
reenvía.
a) Implemente las clases Tweet, Retweet y DirectMessage escogiendo la jerarquía más adecuada.
Añada los atributos que se especifican en el enunciado y establezca su visibilidad.
• Reutilice todo el código que pueda. Para el atributo time, se recomienda utilizar la clase Date de la
librería estándar de Java.
b) Implemente los constructores de las clases reutilizando al máximo todo el código disponible.
• Además, compruebe las restricciones de datos (por ejemplo, el constructor debería lanzar
• Recuerde que la librería estándar tiene una función len(string) que devuelve la longitud de un
string.
c) Implemente el método __str__(self) en las tres clases, reutilizando al máximo todo el código
disponible. Suponga que las clases date y UserAccount ya tiene este método implementado
correctamente.
65
razonamiento y, si cree que hay que modificarlos, explique también cómo lo haría.
razonamiento y, si cree que hay que modificarlo, explique también cómo lo haría.
import java.util.Date;
56
receiver.getAlias() + ")";
}
}
a) En la jerarquía de clases, se utiliza la herencia para definir las clases Retweet y DirectMessage
como subclases de la clase base Tweet. Esto permite reutilizar código y heredar los atributos y
métodos de la clase Tweet.
c) El método toString() se implementa en las tres clases para representar los tweets, retweets y
mensajes directos en forma de texto. Se reutiliza el código de la clase base Tweet y se agrega la
información específica de cada tipo de tweet.
No es necesario modificar los atributos timeline y tweets de la clase UserAccount para que
contengan elementos de la clase Retweet. La clase Retweet es una subclase de la clase Tweet, por lo
que se puede almacenar en los atributos existentes sin necesidad de modificaciones adicionales.
67
Suponga que la clase Utils contiene los métodos: leerCadena, leerEntero, leerFloat y leerDouble
correctamente implementados. Utilícelos para leer por consola como lo hace en los ejercicios de
clase.
Implemente una clase main que permita al iniciarse Cargue una lista de usuarios de un
fichero. Utilice las estructuras de datos que considere más adecuadas y justifique su uso. Puede
crear las funciones y métodos que considere convenientes o necesarias en las clases
correspondientes previamente solicitadas. Sólo debe especificar qué clases son en caso de que lo
haga, no debe redifinir la clase completa. Posteriormente habrá un menú que permita:
a) Cargar un usuario en memoria realizando una búsqueda secuencial con centinela sobre el
conjunto de datos previamente cargado. Puede crear las funciones y métodos que considere
convenientes o necesarias en las clases correspondientes previamente solicitadas. Sólo debe
especificar qué clases son en caso de que lo haga, no debe redifinir la clase completa.
b) Publicar un tweet por el usuario cargado previamente en memoria. Puede asumir que siempre
habrá un usuario cargado en memoria. Debe gestionar que si el tweet sobrepasa los 140 caracteres
se debe mostrar un error al usuario. Para ello, utilice la gestión de excepciones adecuada y no haga
comprobaciones extra de entrada/salida. Puede crear las clases, funciones y métodos que considere
convenientes o necesarias. Sólo debe especificar qué clases son en caso de que las modifique, no
debe redifinir la clase completa. Utilice las estructuras de datos que considere más adecuadas y
justifique su uso.
c) Ordenar los diferentes usuarios en base a 2 criterios diferentes: 1. El email de forma ascendente 2.
El nombre de usuario de forma descendente.
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
// Menú principal
boolean exit = false;
while (!exit) {
System.out.println("Seleccione una opción:");
System.out.println("1. Cargar un usuario en memoria");
System.out.println("2. Publicar un tweet");
68
System.out.println("3. Ordenar usuarios por email
(ascendente)");
System.out.println("4. Ordenar usuarios por nombre de usuario
(descendente)");
System.out.println("5. Salir");
69
String username = Utils.leerCadena();
if (user != null) {
// Cargar el usuario en memoria para su posterior uso
// usuarioCargado = user;
System.out.println("Usuario cargado en memoria: " +
user.getUsername());
} else {
System.out.println("El usuario no existe en la lista.");
}
}
try {
// Validar la longitud del mensaje
if (message.length() > 140) {
throw new IllegalArgumentException("El mensaje supera los
140 caracteres.");
}
70
descendente
Collections.sort(userList,
Comparator.comparing(UserAccount::getUsername).reversed());
H
1. Aprovechando el tirón de la última temporada de la serie, HBO quiere sacar al mercado el juego :
The Iron Throne®, en el que cualquier persona podrá conquistar el Trono de Hierro y gobernar los
Sietes Reinos que conforman Poniente.
Para llevar a cabo este proyecto, han contratado al estudio de programación en el que usted está
haciendo prácticas (!!qué suerte, con lo que le gusta la serie!!), y un creativo de la empresa ha
comenzado a describir las especicaciones que tienen en mente:
Los nuevos jugadores deben registrarse en la aplicación antes de comenzar a desarrollar las partidas.
Cada Jugador tiene un alias (que servirá para identicarle), la fecha en la que se registró́, pertenecerá
a alguna de las grandes casas del juego (Arryn, Baratheon, Lannister o Stark) y acumulará un número
de monedas que consigue a través de la colección de capitales de los reinos que domina. Por último,
tendrá una colección de armas que podrá utilizar para conquistar otras capitales (lo que le acercará a
poseer el Trono de Hierro).
Todas las capitales tienen un número que utilizan como identicador, pertenecen a una familia y una
población que determina su riqueza y defensa.
Son tierras fértiles y fácilmente cultivables. La población inicial de esta ciudad es la siguiente: 50
agricultores, 40 soldados y 10 comerciantes.
Invernalia: Es la capital de El Norte, donde se asienta la fortaleza de la casa Stark. Tiene un gran
cúmulo de soldados, puesto que debe defender al resto de reinos de las amenazas que provengan
de más allá del muro.
Nido de Águilas: Es la capital de El Valle de Arryn, residencia de la casa Arryn. Esta ciudad se
caracteriza por tener un comercio y agricultura moderados. Inicialmente, la ciudad cuenta con: 40
agricultores, 20 soldados y 40 comerciantes. Roca Casterly: Es la capital de Las Tierras del Oeste,
asentamiento de la rama principal de la casa Lannister. Son tierras muy ricas en oro, origen del
poder de esta casa.
Durante el transcurso de la partida, los jugadores irán acumulando monedas en función de las
capitales que controlen. Utilizarán esas monedas para alimentar a su población y para comprar
armas. Dichas armas le servirán para conquistar nuevas capitales que no posean. El objetivo nal de
cada jugador es conquistar las capitales de todos los reinos, haciendo sus vasallos a los habitantes de
cada una de las casas y las ciudades. El jugador que primero llegue a controlar todos los reinos,
tomará su lugar en el Trono de Hierro.
72
- Las clases Agricultor, Soldado, Comerciante y MenorEdad heredan de la clase Habitante;
suponga que todas ya están correctamente implementadas.
- Suponga que también existe una clase Utilidades que contiene el método public static int
generaIdentificador() que genera el identicador de cada una de las capitales.
Genere los constructores necesarios para inicializar los atributos de las capitales. Deberán ser
acordes con las restricciones de población indicadas anteriormente.
No se permite el uso de arrays, utilice la colección de Java que mejor se adapte al problema y
reutilice todo el código que pueda. Implemente los siguientes métodos:
- En la clase Capital, implemente los métodos necesarios para añadir y eliminar un habitante a
la población (ya sea agricultor, comerciante, soldado o menor de edad).
- Dena, en Capital, el método: public void generarPoblacion().
La implementación de este método depende del tipo concreto de capital. Cada vez que se ejecute
este método, se añadirá un nuevo habitante a la población de la ciudad. Para ello, se generarán dos
números aleatorios (men y tipo) entre 0 y 100. Una vez obtenidos ambos números:
1. Si men < 50 ó no hay ningún habitante menor de edad, se crea un habitante menor de edad.
Cada tipo de capital denirá los valores umbrales de tipo, de tal manera que se mantenga la
proporción de habitantes iniciales. Por ejemplo, para BastionDeTormentas, si 0 ≤ tipo < 50 se gene-
rará un agricultor, si 50 ≤ tipo < 90 se generará un soldado y si 90 ≤ tipo ≤ 100 se generará un nuevo
comerciante.
- Si tiene que añadir habitantes a la población, utilice el/los método/s que ha implementado
anteriormente.
Implemente, en la clase Capital, un método que permita calcular cuántas monedas son necesarias
para alimentar a toda su población.
Cada habitante de la población tiene unas necesidades alimenticias, por lo que deberá calcular el
coste total reuniendo los costes parciales de cada uno de ellos.
- Suponga que la clase Habitante tiene un método public int costeAlimento() que devuelve la
cantidad de monedas necesarias para alimentar a dicho habitante.
- Suponga que la clase Arma y todas las clases del apartado anterior se encuentran
correctamente programadas.
73
- Tenga en cuenta que cuando se crea un jugador: tiene 10000 monedas iniciales; en la
colección de capitales de los reinos que domina está la capital asociada a la casa que ha
seleccionado; la colección de armas se encuentra vacía.
No se permite el uso de arrays. Utilice la colección de Java que mejor se adapte al problema.
Este método permite a un jugador alimentar todas sus poblaciones. Para ello, se calcularán las
monedas necesarias para proporcionar alimento a todas las capitales que controla y, esa cantidad,
se descontará de las monedas que posee el usuario.
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
class Utilidades {
public static int generaIdentificador() {
Random rand = new Random();
return rand.nextInt(1000); // Genera un identificador
aleatorio entre 0 y 999
}
}
class Capital {
private int identificador;
74
private String familia;
private int poblacion;
private List<Agricultor> agricultores;
private List<Soldado> soldados;
private List<Comerciante> comerciantes;
private List<MenorEdad> menoresEdad;
75
} else if (habitante instanceof MenorEdad) {
this.menoresEdad.remove(habitante);
}
this.poblacion--;
}
76
public Invernalia() {
super("Stark", 20, 70, 10);
}
}
class Arma {
// Implementación de la clase Arma
}
class Jugador {
private String alias;
private String fechaRegistro;
private String casa;
private int monedas;
private List<Capital> capitales;
private List<Arma> armas;
77
public void alimentarPoblaciones() throws NoMonedasException {
int costeTotal = 0;
for (Capital capital : this.capitales) {
costeTotal += capital.costeAlimentoPoblacion();
}
2. Una de las partes más interesantes de : The Iron Throne®es, precisamente, las armas que los
jugadores podrán utilizar para conquistar las distintas capitales de los reinos que desea. Cada Arma
tiene como características: un identicador único, el precio por el que un usuario puede adquirirla, el
tipo de medio en el que se utiliza (terrestre, aéreo, acuático), la composición de sus proyectiles
(fuego, acero valirio, piedra), las unidades de daño que provoca, el jugador que la atesora y la capital
en la que se encuentra situada.
Espada: arma terrestre, con un precio de 1000 monedas, compuesta por acero valirio y que provoca
un daño de 500 unidades.
Catapulta: arma terrestre, con un precio de 2500 monedas, compuesta por piedra y que provoca un
daño de 1500 unidades.
Carabela: arma acuática, con un precio de 5000 monedas, compuesta por fuego y que provoca un
daño de 3000 unidades.
Dragón: arma aérea, con un precio de 20000 monedas, compuesta por fuego y que provoca un daño
de 5000 unidades.
(a) Implemente las clases necesarias para representar los distintos tipos de arma en el videojuego.
78
Añada los atributos que se especican en el enunciado y establezca su visibilidad.
(b) Implemente los constructores de las clases reutilizando al máximo todo el código disponible.
- Existirá un constructor que recibirá como parámetros los necesarios para inicializar el arma,
a excepción del jugador y la capital.
- Existirá un constructor que esté parametrizado con todos los parámetros necesarios
(incluyendo el jugador y la capital).
◦ Lance una excepción si la capital en la que se establece un arma no se encuentra entre las
controladas por el jugador. Suponga que la clase Jugador ya tiene el método public boolean
duenioCapital(Capital c) correctamente implementado.
(c) Implemente el método toString de tal manera que se muestren todas las carac- terísticas del
arma. Este método deberá estar disponible para todos los tipos de arma. Reutilice al máximo todo el
código disponible.
import java.util.Date;
class Arma {
private String identificador;
private int precio;
private String tipoMedio;
private String composicionProyectiles;
private int unidadesDanio;
private Jugador jugador;
private Capital capital;
79
unidadesDanio);
if (!jugador.duenioCapital(capital)) {
throw new CapitalNoControladaException("El jugador no controla
la capital en la que se establece el arma.");
}
this.jugador = jugador;
this.capital = capital;
}
@Override
public String toString() {
return "Arma{" +
"identificador='" + identificador + '\'' +
", precio=" + precio +
", tipoMedio='" + tipoMedio + '\'' +
", composicionProyectiles='" + composicionProyectiles +
'\'' +
", unidadesDanio=" + unidadesDanio +
", jugador=" + jugador.getAlias() +
88
", capital=" + capital.getNombre() +
'}';
}
}
3. A lo largo del juego, los jugadores de : The Iron Throne®deben conocer cuáles son las capitales
más ricas o más poderosas que poseen, para defenderlas o para lanzar ataques desde ellas.
Para ello, en la clase Jugador (especicada en el ejercicio 1 apartado b), deberíamos incluir los
métodos:
81
public void muestraCapitalesPorPoder(List<Capital> capital)
El cálculo de la riqueza y el poder de una capital se realiza utilizando las siguientes fórmulas:
En este ejercicio se le solicitará que implemente métodos en las clases Jugador y Capital, no es
necesario que escriba las clases de nuevo, sólo escriba aquellas líneas o métodos que cambien o
nuevos métodos auxiliares que crea adecuado añadir.
Suponga que la clase Capital ya tiene los métodos public String toString() y public int hashCode()
implementados.
Para facilitar la implementación de las fórmulas, suponga también que la clase Capital ya tiene
implementados correctamente los métodos:
(a) En la clase Capital implemente los métodos public int getRiqueza() y public int getPoder() que
calcule la riqueza y el poder, respectivamente, de una capital, basándose en las fórmulas arriba
expuestas.
(b) Dena como método de ordenación natural, la ordenación de las capitales por riqueza. Para ello,
implemente la interfaz Comparable<Capital> en la clase Capital. Con ese n, además de
añadir/modicar lo que crea necesario en la clase Capital, implemente el método:
Este método deberá comparar, dos capitales teniendo en cuenta la riqueza de cada una de ellas.
Así, el método compareTo devolverá: 0 si ambas capitales tienen la misma riqueza, un entero
negativo si capital2 es más rica, o un entero positivo si capital2 es más pobre.
(c) Implemente el método equals en la clase Capital. Utilice el identicador de cada capital.
Para ordenar la lista de capitales opte por una de las siguientes opciones: utilice un TreeSet, utilice
algún método de la clase Collections ó implemente algún método de ordenación que conozca.
Para la implementación de este método utilice un TreeSet y cree una clase que implemente la
interfaz Comparator<Capital> que posee el método:
82
public int compare(Capital capital1, Capital capital2)
En este método compare ambas capitales mediante su poder. El método devol- verá: 0 si ambas
capitales tienen el mismo poder, un entero negativo si capital2 es más poderosa, o un entero
positivo si capital2 es más débil.
(f) Implemente el método existeCapital de forma que, dada una lista de capitales y una capital,
devuelva cierto si la capital se encuentra en la lista.
Para realizar la búsqueda opte por una de las siguientes opciones: utilice algún método de la clase
Collections ó implemente algún método de búsqueda que conozca. En ningún caso utilice el método
contains de la lista.
a)
b)
@Override
public int compareTo(Capital capital2) {
return Integer.compare(this.getRiqueza(), capital2.getRiqueza());
}
}
c)
public class Capital {
// ...
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Capital capital = (Capital) o;
return Objects.equals(identificador, capital.identificador);
}
}
d)
83
public void muestraCapitalesPorRiqueza(List<Capital> capitales) {
Collections.sort(capitales);
for (Capital capital : capitales) {
System.out.println(capital);
}
}
e)
import java.util.Comparator;
import java.util.TreeSet;
f)
public boolean existeCapital(List<Capital> capitales, Capital capital) {
for (Capital c : capitales) {
if (c.equals(capital)) {
return true;
}
}
return false;
}
84
Respuestas correctas suman 0.5 puntos. Se debe responder a 4 preguntas de las 5 disponibles. Las 4
preguntas a contestar son elegidas libremente por el estudiante.
1) Debemos diseñar un programa para un sistema de dispositivos portables aplicados en
telemedicina que contienen una cantidad de memoria muy limitada (4 KB). Tenemos diferentes
alternativas que hacen diferente uso de la computación y la memoria.
b) La segunda de ellas tiene un orden de complejidad computacional O(1) pero lo hace mediante una
técnica denominada para guardar muchos datos en memoria, por lo que ocupa un espacio de S(N2).
c) La tercera y última tiene una complejidad computacional de O(log2(N)) y ocupa un poco más de
espacio en memoria S(O(log2(N))).
b) La segunda alternativa tiene un orden de complejidad computacional O(1), lo que implica que el
tiempo de ejecución es constante e independiente del tamaño de los datos de entrada. Sin embargo,
utiliza una técnica para guardar muchos datos en memoria, lo que ocupa un espacio de S(N^2). Dado
que el tamaño de datos de entrada es de 64 KB, ocupar un espacio de S(N^2) podría resultar muy
ineficiente en términos de memoria. A pesar de tener una complejidad O(1), el alto consumo de
memoria puede ser problemático en un sistema con recursos limitados.
c) La tercera alternativa tiene una complejidad computacional de O(log2(N)) y ocupa un poco más de
espacio en memoria, S(O(log2(N))). La complejidad O(log2(N)) indica que el tiempo de ejecución
aumentará de manera logarítmica con el tamaño de los datos de entrada, lo que significa una
eficiencia bastante buena en términos de tiempo. En cuanto al espacio en memoria, ocupará un
poco más de espacio a medida que aumenta el tamaño de los datos, pero la complejidad logarítmica
sugiere que el aumento no será exponencial. Dado que el programa tiene una cantidad limitada de
memoria (4 KB) y una entrada de datos de 64 KB, una complejidad logarítmica puede ser una opción
más eficiente tanto en términos de tiempo como de espacio.
85
Crear una clase que represente un número complejo que permita, al menos, sumar, restar y
multiplicar números complejos. Proporcionar un constructor por defecto, un constructor de copia
(esto es, un constructor al que se le pasa una instancia de la clase número racional y crea otro
número racional idéntico), y otro que permita indicar los valores de la parte real y de la parte
imaginaria. Usa la clase desde un programa de ejemplo (main).
public NumeroComplejo() {
parteReal = 0.0;
parteImaginaria = 0.0;
}
86
return new NumeroComplejo(real, imaginaria);
}
@Override
public String toString() {
return parteReal + " + " + parteImaginaria + "i";
}
87
a) (1.0 puntos) Escribir un método estático que busque raíces de una función empleando el
algoritmo de Newton-Raphson para encontrar una raíz de una función. Este método parte de una
estimación inicial de la raíz, x0, que deberá ser un parámetro del método estático y va calculando
aproximaciones sucesivas al valor de la misma utilizando la fórmula:
xi+1=xi-f(xi)/f'(xi)
siendo f(xi) el valor de la función en el punto xi y siendo f'(xi) el valor de la derivada de la función en
el punto xi. Al ser un método iterativo, es necesario tener un criterio para que termine. Puede
establecerse como criterio de terminación que la diferencia entre f(x) y 0 sea menor que 0.001.
El método estático para hallar las raíces recibirá como argumentos la función y el valor inicial sobre
el cual se comenzará a aplicar la fórmula de Newton-Raphson:
b) (0.75 puntos) Por seguridad, se debe establecer un número máximo de iteraciones para que el
algoritmo termine aunque no converja a una solución (de lo contrario, al ejecutarlo en el ordenador
éste podría quedarse bloqueado). No deberá aplicarse la fórmula recursiva de Newton-Raphson más
de 1000 veces. En caso de tener que aplicar más de 1000 veces la fórmula recursiva, deberá lanzarse
una excepción que indique que no converge el algoritmo.
c)(0.75 puntos) Crea una implementación de FunctionInterface para funciones del tipo ax2 + bx+c y
ex –bx y busca raíces para dos ecuaciones de este tipo con parámetros a=1, b=2, c=-4 desde el
método main. Las clases que implementen ambas funciones deberán tener un constructor al que se
le pasen sus respectivos parámetros (a, b y c; y b, respectivamente).
88
float xiPlus1 = xi - fxi / fxiPrime;
xi = xiPlus1;
}
89
}
98
Escribe un programa que lea los datos que ha escrito este otro programa y los muestre por consola.
El programa deberá cerrar adecuadamente los recursos que haya empleado para la lectura de los
datos, cosa que no se hace correctamente en el programa que escribe los datos.
import java.io.*; import java.util.Date;
objectOutputStream.writeObject(new Date()); }
dataOutputStream.writeInt(i); }
}}
import java.io.*;
import java.util.Date;
try {
File file1 = new File("C:/a/datos.dat");
File file2 = new File("C:/a/objetos.dat");
91
boolean booleanValue = dataInputStream.readBoolean();
double doubleValue = dataInputStream.readDouble();
System.out.println("Int values:");
for (int i = 0; i < 10; i++) {
int intValue = dataInputStream.readInt();
System.out.print(intValue + " ");
}
System.out.println();
System.out.println("Float values:");
for (int i = 0; i < 10; i++) {
float floatValue = dataInputStream.readFloat();
System.out.print(floatValue + " ");
}
System.out.println();
System.out.println("Dates:");
for (int i = 0; i < 10; i++) {
Date date = (Date) objectInputStream.readObject();
System.out.println(date);
}
92
Escribe un programa que lea los datos que ha escrito este otro programa y los muestre por consola.
Antes de mostrar los datos por pantalla éstos deberán convertirse al tipo de dato original; es decir,
no se debe leer texto del archivo y escribir texto a la consola. Debe leerse texto del archivo,
convertirse al tipo de dato primitivo correspondiente, y mostrarse el dato en la consola. El programa
deberá cerrar adecuadamente los recursos que haya empleado para la lectura de los datos, cosa que
no se hace correctamente en el programa que escribe los datos.
import java.io.*;
printWriter.println("Hello"); printWriter.println(3.6F);
printWriter.println(true); printWriter.println(5.5);
printWriter.close(); }
import java.io.*;
try {
FileReader fileReader = new FileReader(file);
bufferedReader = new BufferedReader(fileReader);
String line;
while ((line = bufferedReader.readLine()) != null) {
Object value = convertToOriginalType(line);
System.out.println(value);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
// Cerrar el recurso adecuadamente
try {
if (bufferedReader != null) {
bufferedReader.close();
}
} catch (IOException e) {
93
e.printStackTrace();
}
}
}
94
En la aplicación de contactos de un móvil con sistema operativo Android, el usuario tie- ne una
Agenda que incluye un array de contactos. Cada Contacto almacena, entre otras características: el
nombre, el apodo, una dirección de email, un número de teléfono y un valor lógico que indica si las
llamadas de este contacto hay que silenciarlas. Además, los contactos pueden clasicarse como:
trabajo, amigo, familia. En la aplicación también se distinguen los contactos favoritos, a los que se
permite añadir un domicilio y una fecha de cumpleaños.
A lo largo de este ejercicio, añada, si lo necesita, los métodos auxiliares que crea convenientes para
estructurar su código correctamente. En base a estas especicaciones se solicita que:
Suponga que ya tiene una clase Email correctamente implementada y que su constructor por
defecto crea un email por defecto consistente.
Deberá contener los siguientes atributos (elija el tipo más adecuado e implemente tipos
enumerados si los necesita): nombre, apodo, dirección de email, número de teléfono, silenciado y
clasicación.
Programe un constructor que inicialize los atributos con valores por defecto (asig- ne los valores por
defecto que considere oportunos y justifíquelos si lo considera necesario).
Programe un constructor que reciba todos los datos necesarios para crear un con- tacto.
Programe getters y setters para los atributos nombre, número de teléfono y clasicación.
Programe el método public void silenciar(). Este método anota que las lla- madas de este contacto
hay que silenciarlas.
Programe un método que permita obtener un String con la representación del contacto.
Tenga en cuenta que, el usuario siempre debe contener un número de teléfono válido, en caso
contrario, se debería lanzar una excepción indi- cándolo. Los números de teléfono válidos comienzan
por 3 y tienen una longitud de 6 números
Deberá contener los siguientes atributos (elija el tipo más adecuado): contactos. Programe un
constructor por defecto para esta clase.
Programe el método public void silenciaContacto(String n) que se utiliza para silenciar al contacto de
la agenda que tiene como nombre n.
Programe el método public String nombreContactos() que devuelve un String con los nombres de
todos los contactos de la agenda.
Para realizar este apartado, puede dar por supuesto que ha implementado correc- tamente la clase
Contacto del apartado anterior.
95
Añada, a los atributos propios de un Contacto, los atributos propios de Contac- toFavorito: domicilio
y cumpleaños. Para cada uno, use el tipo de dato que crea más adecuado1.
Implemente un constructor que reciba todos los parámetros necesarios para gene- rar un
ContactoFavorito. Reutilice al máximo todo el código disponible.
Implemente el método toString para que se muestren todas las características de cada contacto
favorito. Reutilice al máximo todo el código disponible.
Para realizar este apartado, puede dar por supuesto que ha implementado correc- tamente las
clases Contacto y Agenda de los apartados anteriores.
(d) Desearíamos almacenar contactos del tipo ContactoFavorito en la clase Agenda. Des- criba cómo
podríamos conseguirlo.
Si cree que es necesario realizar algún cambio, indique e implemente las líneas que deberían ser
modicadas (no lo haga sobre la solución que ha dado para Agenda en el apartado anterior).
(e) Desearíamos guardar la Agenda de manera persistente. Para ayudar a la realización de este
ejercicio se facilita determinado código en su parte nal. Se deben cerrar los recursos
adecuadamente.
Según el tipo de ujo de salida seleccionado, represente de forma gráca el formato de dicho chero
especique las modicaciones necesarias a realizar en las clases Agenda, Contacto y ContactoFavorito
respectivamente.
Crea una función auxiliar que recibiendo una agenda y un nombre de chero, guarde los datos de la
agenda en dicho chero. Asume que se puede sobreescribir el chero cada vez que se guarda una
agenda y gestione todas las excepciones mediante el relanzamiento de forma explícita en la
cabecera.
import java.util.ArrayList;
import java.util.List;
enum Clasificacion {
TRABAJO,
AMIGO,
FAMILIA
}
class Contacto {
private String nombre;
private String apodo;
private String direccionEmail;
private String numeroTelefono;
private boolean silenciado;
private Clasificacion clasificacion;
public Contacto() {
96
this.nombre = "";
this.apodo = "";
this.direccionEmail = "";
this.numeroTelefono = "";
this.silenciado = false;
this.clasificacion = null;
}
97
this.silenciado = true;
}
@Override
public String toString() {
return "Contacto{" +
"nombre='" + nombre + '\'' +
", apodo='" + apodo + '\'' +
", direccionEmail='" + direccionEmail + '\'' +
", numeroTelefono='" + numeroTelefono + '\'' +
", silenciado=" + silenciado +
", clasificacion=" + clasificacion +
'}';
}
}
class Agenda {
private List<Contacto> contactos;
public Agenda() {
this.contactos = new ArrayList<>();
}
98
}
return nombres.toString().trim();
}
}
public ContactoFavorito() {
super();
this.domicilio = "";
this.cumpleanios = "";
}
@Override
public String toString() {
return super.toString() +
", domicilio='" + domicilio + '\'' +
", cumpleanios='" + cumpleanios + '\'';
}
}
(d) Para almacenar contactos del tipo ContactoFavorito en la clase Agenda, se puede modificar la
lista de contactos para que pueda contener tanto objetos de tipo Contacto como objetos de tipo
ContactoFavorito. Esto se puede lograr utilizando polimorfismo, ya que la clase ContactoFavorito es
una subclase de Contacto. Por lo tanto, la lista contactos en la clase Agenda puede ser del tipo
List<Contacto>, lo que permite almacenar tanto objetos de Contacto como objetos de
ContactoFavorito.
(e) Para guardar la agenda de manera persistente, se puede utilizar un flujo de salida de tipo
ObjectOutputStream. Este flujo de salida nos permite escribir objetos en un archivo. Para cerrar
adecuadamente los recursos, podemos utilizar un bloque try-with-resources, que se encargará de
cerrar automáticamente los recursos al finalizar su uso. Aquí tienes una función auxiliar que guarda
los datos de la agenda en un archivo:
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
99
public class AgendaPersistence {
objectOutputStream.writeObject(agenda);
}
}
}
El formato del archivo de salida dependerá de la forma en que se escriban los datos en el archivo. En
este caso, se utiliza ObjectOutputStream, que guarda los objetos en un formato binario. Por lo tanto,
el archivo de salida contendrá los datos de la instancia de Agenda y los objetos relacionados en un
formato binario. No es posible representar el formato binario de manera gráfica.
100
EJERCICIOS
EXTRA
Crea una clase Estudiante con los atributos nombre y edad. Luego, crea una lista de estudiantes y
ordénalos por edad de forma ascendente utilizando el método Collections.sort(). Muestra por
pantalla la lista de estudiantes ordenada
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.*;
@Override
public String toString() {
return "Nombre: "+nombre+"\nEdad: "+edad+"\n";
}
101
edad=Integer.parseInt(teclado.readLine());
Estudiantes estudiante= new
Estudiantes(nombre,edad);
clase.add(estudiante);
break;
case 2:
Collections.sort(clase,
Comparator.comparing(Estudiantes::getEdad));
System.out.println("Clase: ");
for (Estudiantes estudiantes: clase){
System.out.println(estudiantes);
}
break;
default:
return;
}
}while(opcion!=3);
}catch (IOException e){
System.out.println(e.getMessage());
}
}
public static void menu(){
System.out.println("1.Añadir estudiante\n2.Ordenar\nOPCION:");
}
}
102
Supongamos que necesitamos desarrollar un programa en Java para llevar un control de ventas de
una tienda. Cada venta está compuesta por un código de producto, una cantidad vendida y un precio
unitario. Se pide implementar una clase Venta con los siguientes métodos:
- Constructor: que recibe como parámetros el código de producto, la cantidad vendida y el
precio unitario. Métodos getters y setters para los atributos de la venta.
- Método calcularTotal: que devuelve el total de la venta (cantidad vendida x precio unitario).
- Método toString: que devuelve una cadena con los datos de la venta en el formato
"Producto: [código], cantidad: [cantidad], precio unitario: [precio], total: [total]".
Además, se pide implementar una clase Tienda que contenga una lista de ventas y que tenga los
siguientes métodos:
- Constructor: que inicializa la lista de ventas.
- Método agregarVenta: que recibe como parámetros los datos de una venta y la agrega a la
lista.
- Método calcularTotalVentas: que devuelve el total de todas las ventas realizadas en la
tienda.
- Método promedioVentas: que devuelve el promedio de ventas realizadas en la tienda.
- Método ordenarPorPrecio: que ordena la lista de ventas por precio unitario de forma
ascendente.
- Método ordenarPorCodigo: que ordena la lista de ventas por código de producto de forma
ascendente.
- Método toString: que devuelve una cadena con los datos de todas las ventas realizadas en la
tienda en el formato "Venta [i]: Producto: [código], cantidad: [cantidad], precio unitario:
[precio], total: [total]".
public class Venta {
private String codigo;
private int cantidad_venta;
private int precio;
public Venta(String codigo, int cantidad_venta, int precio){
this.codigo=codigo;
this.cantidad_venta=cantidad_venta;
this.precio=precio;
}
103
public void setCodigo(String codigo) {
this.codigo = codigo;
}
@Override
public String toString() {
return "Codigo: "+codigo+"Cantidad venta:
"+cantidad_venta+"Cantidad unidad: "+precio;
}
public static int cantidadTotal(int cantidad_unida, int
cantidad_venta){
return cantidad_unida*cantidad_venta;
}
}
import java.util.Comparator;
import java.util.List;
104
Supongamos que tenemos un conjunto de figuras geométricas (círculos, cuadrados y triángulos)
representadas por objetos de sus respectivas clases: Circulo, Cuadrado y Triangulo. Se pide
implementar una clase ColeccionFiguras que contenga un arreglo de figuras y que tenga los
siguientes métodos:
• Constructor: que recibe como parámetro el tamaño del arreglo.
• Método agregarFigura: que recibe como parámetro una figura y la agrega al arreglo.
• Método areaTotal: que devuelve el área total de todas las figuras en la colección.
• Método promedioPerimetro: que devuelve el promedio de los perímetros de todas las figuras en la
colección.
• Método toString: que devuelve una cadena con los datos de todas las figuras en la colección.
• Método toCSV: que devuelve una cadena con los datos de todas las figuras en la colección.
import java.util.ArrayList;
import java.util.List;
public ColeccionFiguras() {
figuras = new ArrayList<>();
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
for (Figura figura : figuras) {
sb.append(figura.toString()).append("\n");
}
return sb.toString();
}
155
StringBuilder sb = new StringBuilder();
for (Figura figura : figuras) {
sb.append(figura.toCSV()).append("\n");
}
return sb.toString();
}
106
}
public void setLado2(double lado2) {
this.lado2 = lado2;
calcularArea();
calcularPerimetro();
}
public double getLado3() {
return lado3;
}
107
public double calcularPerimetro() {
setPerimetro(4 * lado);
return getPerimetro();
}
}
public class Circulo extends Figura {
private double radio;
108
Supongamos que se desea desarrollar una aplicación para una biblioteca que permita llevar el
control de los préstamos de libros. Se pide implementar las siguientes clases:
• Una clase Libro que contenga los siguientes atributos: Título del libro (String), Autor del libro
(String), ISBN del libro (String), Cantidad de ejemplares del libro (int).
• Una clase Usuario que contenga los siguientes atributos: Nombre del usuario (String) y DNI del
usuario (String).
• Una clase Prestamo que contenga los siguientes atributos: Libro prestado (objeto de la clase Libro),
Usuario que ha solicitado el préstamo (objeto de la clase Usuario), Fecha de inicio del préstamo
(objeto de la clase LocalDate), Fecha de fin del préstamo (objeto de la clase LocalDate).
• Una clase Biblioteca que contenga los siguientes métodos:
o Método agregarLibro: que recibe como parámetro un objeto de la clase Libro y lo agrega a una
lista de libros disponibles.
o Método agregarUsuario: que recibe como parámetro un objeto de la clase Usuario y lo agrega a
una lista de usuarios registrados en la biblioteca.
o Método prestarLibro: que recibe como parámetro el ISBN de un libro y el DNI de un usuario y, si el
libro está disponible y el usuario está registrado en la biblioteca, crea un objeto de la clase Prestamo
y lo agrega a una lista de préstamos realizados.
o Método devolverLibro: que recibe como parámetro el ISBN de un libro y, si existe un préstamo
asociado al libro y aún no ha vencido la fecha de devolución, actualiza la cantidad de ejemplares
disponibles y elimina el préstamo de la lista de préstamos realizados.
o Método listarLibrosDisponibles: que devuelve una cadena con el título, autor, ISBN y cantidad de
ejemplares disponibles de todos los libros disponibles en la biblioteca.
o Método listarUsuariosRegistrados: que devuelve una cadena con el nombre y DNI de todos los
usuarios registrados en la biblioteca.
o Método listarPrestamos: que devuelve una cadena con el título, autor, ISBN, nombre y DNI de los
usuarios y fechas de inicio y fin de todos los préstamos realizados en la biblioteca.
oMétodo para ordenar alfabeticamente los libros
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
public Biblioteca() {
librosDisponibles = new ArrayList<>();
usuariosRegistrados = new ArrayList<>();
prestamosRealizados = new ArrayList<>();
}
109
}
10
Usuario usuario = prestamo.getUsuario();
sb.append("Título: ").append(libro.getTitulo())
.append(", Autor: ").append(libro.getAutor())
.append(", ISBN: ").append(libro.getIsbn())
.append(", Nombre Usuario: ").append(usuario.getNombre())
.append(", DNI Usuario: ").append(usuario.getDni())
.append(", Fecha Inicio: ").append(prestamo.getFechaInicio())
.append(", Fecha Fin: ").append(prestamo.getFechaFin())
.append("\n");
}
return sb.toString();
}
111
this.isbn = isbn;
this.cantidadEjemplares = cantidadEjemplares;
}
import java.time.LocalDate;
112
public LocalDate getFechaFin() {
return fechaFin;
}
}
public class Usuario {
private String nombre;
private String dni;
113
Se te pide que implementes una clase en Java llamada CardDeck que represente una baraja de
cartas. La clase debe tener los siguientes métodos públicos:
• void shuffle(): método que mezcla las cartas de la baraja.
• Card drawCard(): método que devuelve la carta en la parte superior de la baraja y la elimina de la
misma.
• void returnCard(Card card): método que devuelve una carta a la parte inferior de la baraja. La clase
Card ya está definida y tiene los siguientes atributos:
• String suit: palo de la carta (por ejemplo, "spades", "hearts", "diamonds" o "clubs").
• int rank: valor de la carta (del 1 al 13).
public CardDeck() {
initializeDeck();
}
114
public Card drawCard() {
if (deck.isEmpty()) {
return null; // Si la baraja está vacía, no se puede extraer ninguna
carta
}
115
Supongamos que ahora se te pide que implementes un programa en Java para una tienda online que
venda productos. Cada producto tendrá un código único, nombre, descripción, precio y cantidad
disponible en inventario. Además, deberás implementar una clase CarritoDeCompras que permita a
los clientes añadir productos a su carrito y hacer la compra final. La clase Producto tiene los
siguientes atributos:
• int codigo: código único del producto.
• String nombre: nombre del producto.
• String descripcion: descripción del producto.
• double precio: precio del producto.
• int cantidad: cantidad disponible en inventario del producto.
La clase TiendaOnline tiene los siguientes métodos públicos:
• void agregarProducto(Producto producto): método que agrega un nuevo producto al inventario de
la tienda.
• void eliminarProducto(int codigo): método que elimina un producto del inventario de la tienda.
• Producto buscarProducto(int codigo): método que busca un producto por su código y devuelve un
objeto Producto.
• void imprimirInventario(): método que imprime en consola el inventario actual de la tienda. La
clase
CarritoDeCompras tiene los siguientes métodos públicos:
• void agregarProducto(Producto producto, int cantidad): método que añade una cantidad
determinada de un producto al carrito de compras del cliente.
• void eliminarProducto(Producto producto, int cantidad): método que elimina una cantidad
determinada de un producto del carrito de compras del cliente.
• double calcularTotal(): método que calcula el total de la compra en base a los productos y
cantidades en el carrito de compras.
• void imprimirCarrito(): método que imprime en consola los productos y cantidades en el carrito de
compras del cliente. Se te pide también implementar una clase Descuento que actúe como gestor de
la tienda y permita aplicar descuentos en base a ciertos criterios.
La clase Descuento tiene los siguientes métodos públicos:
• void establecerDescuentoPorCantidad(int cantidad, double descuento): método que establece un
descuento en porcentaje para los productos que se compren en una cantidad determinada.
• void establecerDescuentoPorPrecio(double precio, double descuento): método que establece un
descuento en porcentaje para los productos que tengan un precio superior a un valor determinado.
• double calcularDescuento(Producto producto, int cantidad): método que calcula el descuento a
aplicar a un producto en base a la cantidad que se vaya a comprar.
• double calcularDescuento(Producto producto): método que calcula el descuento a aplicar a un
producto en base a su precio.
import java.util.HashMap;
import java.util.Map;
public TiendaOnline() {
inventario = new HashMap<>();
}
116
public void agregarProducto(Producto producto) {
inventario.put(producto.getCodigo(), producto);
}
117
return precio;
}
@Override
public String toString() {
return "Código: " + codigo + ", Nombre: " + nombre + ", Precio: "
+ precio + ", Cantidad: " + cantidad;
}
}
import java.util.HashMap;
import java.util.Map;
public Descuento() {
descuentoPorCantidad = new HashMap<>();
descuentoPorPrecio = new HashMap<>();
}
118
double descuento = 0.0;
for (Map.Entry<Double, Double> entry :
descuentoPorPrecio.entrySet()) {
double precioLimite = entry.getKey();
double descuentoPorcentaje = entry.getValue();
if (producto.getPrecio() > precioLimite) {
descuento = Math.max(descuento, descuentoPorcentaje);
}
}
return descuento;
}
}
import java.util.HashMap;
import java.util.Map;
public CarritoDeCompras() {
carrito = new HashMap<>();
}
public void agregarProducto(Producto producto, int cantidad) {
int cantidadActual = carrito.getOrDefault(producto, 0);
carrito.put(producto, cantidadActual + cantidad);
}
public void eliminarProducto(Producto producto, int cantidad) {
int cantidadActual = carrito.getOrDefault(producto, 0);
int nuevaCantidad = Math.max(cantidadActual - cantidad, 0);
if (nuevaCantidad > 0) {
carrito.put(producto, nuevaCantidad);
} else {
carrito.remove(producto);
}
}
public double calcularTotal() {
double total = 0.0;
for (Map.Entry<Producto, Integer> entry : carrito.entrySet()) {
Producto producto = entry.getKey();
int cantidad = entry.getValue();
total += producto.getPrecio() * cantidad;
}
return total;
}
119
System.out.println(producto + " - Cantidad: " + cantidad);
} } }
Crearemos una supeclase llamada Electrodomestico con las siguientes características:
. Sus atributos son precio base, color, consumo energético (letras entre A y F) y peso. Indica que se
podrán heredar.
. Por defecto, el color sera blanco, el consumo energético sera F, el precioBase es de 100 € y el peso
de 5 kg. Usa constantes para ello.
. Los colores disponibles son blanco, negro, rojo, azul y gris. No importa si el nombre esta en
mayúsculas o en minúsculas.
Los constructores que se implementaran serán :
- Un constructor por defecto.
- Un constructor con el precio y peso. El resto por defecto.
- Un constructor con todos los atributos.
. Los métodos que implementara serán:
- Métodos get de todos los atributos.
- comprobarConsumoEnergetico(char letra): comprueba que la letra es correcta, sino es
correcta usara la letra por defecto. Se invocara al crear el objeto y no sera visible.
- comprobarColor(String color): comprueba que el color es correcto, sino lo es usa el color por
defecto. Se invocara al crear el objeto y no sera visible.
- precioFinal(): según el consumo energético, aumentara su precio, y según su tamaño,
también.
120
- precioFinal(): si tiene una resolución mayor de 40 pulgadas, se incrementara el precio un
30% y si tiene un sintonizador TDT incorporado, aumentara 50 €. Recuerda que las
condiciones que hemos visto en la clase Electrodomestico también deben afectar al precio.
Ahora crea una clase ejecutable que realice lo siguiente:
. Crea un array de Electrodomesticos de 10 posiciones.
. Asigna a cada posición un objeto de las clases anteriores con los valores que desees. Ahora, recorre
este array y ejecuta el método precioFinal().
. Deberás mostrar el precio de cada clase, es decir, el precio de todas las televisiones por un lado, el
de las lavadoras por otro y la suma de los Electrodomesticos (puedes crear objetos
Electrodomestico, pero recuerda que Television y Lavadora también son electrodomésticos).
Recuerda el uso operador instanceof.
Por ejemplo, si tenemos un Electrodomestico con un precio final de 300, una lavadora de 200 y una
televisión de 500, el resultado final sera de 1000 (300+200+500) para electrodomésticos, 200 para
lavadora y 500 para televisión.
class Electrodomestico {
protected double precioBase;
protected String color;
protected char consumoEnergetico;
protected double peso;
public Electrodomestico() {
this(PRECIO_BASE_DEFECTO, PESO_DEFECTO, COLOR_DEFECTO,
CONSUMO_ENERGETICO_DEFECTO);
}
121
return precioBase;
}
switch (consumoEnergetico) {
case 'A':
precioFinal += 100;
break;
case 'B':
precioFinal += 80;
break;
case 'C':
precioFinal += 60;
break;
case 'D':
precioFinal += 50;
break;
122
case 'E':
precioFinal += 30;
break;
case 'F':
precioFinal += 10;
break;
}
return precioFinal;
}
}
class Lavadora extends Electrodomestico {
private static final double CARGA_DEFECTO = 5.0;
private double carga;
public Lavadora() {
this(PRECIO_BASE_DEFECTO, PESO_DEFECTO, COLOR_DEFECTO,
CONSUMO_ENERGETICO_DEFECTO, CARGA_DEFECTO);
}
@Override
public double precioFinal() {
double precioFinal = super.precioFinal();
122
if (carga > 30) {
precioFinal += 50;
}
return precioFinal;
}
}
class Television extends Electrodomestico {
private static final double RESOLUCION_DEFECTO = 20.0;
private static final boolean SINTONIZADOR_TDT_DEFECTO = false;
public Television() {
this(PRECIO_BASE_DEFECTO, PESO_DEFECTO, COLOR_DEFECTO,
CONSUMO_ENERGETICO_DEFECTO, RESOLUCION_DEFECTO, SINTONIZADOR_TDT_DEFECTO);
}
@Override
public double precioFinal() {
double precioFinal = super.precioFinal();
if (sintonizadorTDT) {
precioFinal += 50;
123
}
return precioFinal;
}
}
double sumaElectrodomesticos = 0;
double sumaLavadoras = 0;
double sumaTelevisores = 0;
sumaElectrodomesticos += electrodomestico.precioFinal();
}
124
Crearemos una clase llamada SERIE con las siguientes características:
. Sus atributos son titulo, numero de temporadas, entregado, genero y creador.
. Por defecto, el numero de temporadas es de 3 temporadas y entregado false. El resto de atributos
serán valores por defecto según el tipo del atributo.
. Los constructores que se implementaran serán:
- Un constructor por defecto.
- Un constructor con el titulo y creador. El resto por defecto.
- Un constructor con todos los atributos, excepto de entregado.
. Los métodos que se implementara serán:
- Métodos get de todos los atributos, excepto de entregado.
- Métodos set de todos los atributos, excepto de entregado.
- Sobrescribe los métodos toString.
Crearemos una clase Videojuego con las siguientes características:
. Sus atributos son titulo, horas estimadas, entregado, genero y compañia.
. Por defecto, las horas estimadas serán de 10 horas y entregado false. El resto de atributos serán
valores por defecto según el tipo del atributo.
. Los constructores que se implementaran serán:
- Un constructor por defecto.
- Un constructor con el titulo y horas estimadas. El resto por defecto.
- Un constructor con todos los atributos, excepto de entregado.
. Los métodos que se implementara serán:
- Métodos get de todos los atributos, excepto de entregado.
- Métodos set de todos los atributos, excepto de entregado.
- Sobrescribe los métodos toString.
Como vemos, en principio, las clases anteriores no son padre-hija, pero si tienen en común, por eso
vamos a hacer una interfaz llamada Entregable con los siguientes métodos:
- entregar(): cambia el atributo prestado a true.
- devolver(): cambia el atributo prestado a false.
- isEntregado(): devuelve el estado del atributo prestado.
- Método compareTo (Object a), compara las horas estimadas en los videojuegos y en las
series el numero de temporadas. Como parámetro que tenga un objeto, no es necesario que
implementes la interfaz Comparable. Recuerda el uso de los casting de objetos.
Implementa los anteriores métodos en las clases Videojuego y Serie. Ahora crea una aplicación
ejecutable y realiza lo siguiente:
. Crea dos arrays, uno de Series y otro de Videojuegos, de 5 posiciones cada uno.
. Crea un objeto en cada posición del array, con los valores que desees, puedes usar distintos
constructores.
. Entrega algunos Videojuegos y Series con el método entregar().
. Cuenta cuantos Series y Videojuegos hay entregados. Al contarlos, devuélvelos.
. Por último, indica el Videojuego tiene más horas estimadas y la serie con mas temporadas.
Muestralos en pantalla con toda su información (usa el método toString()).
125
this.entregado = false;
}
@Override
public int compareTo(Entregable otro) {
return Integer.compare(this.getValorComparativo(),
otro.getValorComparativo());
}
@Override
public String toString() {
return "Título: " + titulo + ", Entregado: " + entregado;
}
@Override
public int getValorComparativo() {
return numTemporadas;
}
126
@Override
public String toString() {
return "Serie - " + super.toString() + ", Num Temporadas: " +
numTemporadas +
", Género: " + genero + ", Creador: " + creador;
}
}
public class Videojuego extends Entregable {
private int horasEstimadas;
private String genero;
private String compañia;
@Override
public int getValorComparativo() {
return horasEstimadas;
}
@Override
public String toString() {
return "Videojuego - " + super.toString() + ", Horas Estimadas: "
+ horasEstimadas +
", Género: " + genero + ", Compañía: " + compañia;
}
}
import java.util.ArrayList;
import java.util.List;
127
videojuegos.add(new Videojuego("Videojuego 1", 20, "Acción",
"Compañía 1"));
videojuegos.add(new Videojuego("Videojuego 2", 10, "Aventura",
"Compañía 2"));
128
Queremos representar con programación orientada a objetos, un aula con estudiantes y un
profesor.
Tanto de los estudiantes como de los profesores necesitamos saber su nombre, edad y sexo. De los
estudiantes, queremos saber también su calificación actual (entre 0 y 10) y del profesor que materia
da.
Las materias disponibles son matemáticas, filosofía y física.
Los estudiantes tendrán un 50% de hacer novillos, por lo que si hacen novillos no van a clase pero
aunque no vayan quedara registrado en el aula (como que cada uno tiene su sitio). El profesor tiene
un 20% de no encontrarse disponible (reuniones, baja, etc.)
Las dos operaciones anteriores deben llamarse igual en Estudiante y Profesor (polimorfismo). El aula
debe tener un identificador numérico, el número máximo de estudiantes y para que esta destinada
(matemáticas, filosofía o física). Piensa que más atributos necesita.
Un aula para que se pueda dar clase necesita que el profesor esté disponible, que el profesor de la
materia correspondiente en el aula correspondiente (un profesor de filosofía no puede dar en un
aula de matemáticas) y que haya más del 50% de alumnos.
El objetivo es crear un aula de alumnos y un profesor y determinar si puede darse clase, teniendo en
cuenta las condiciones antes dichas.
Si se puede dar clase mostrar cuantos alumnos y alumnas (por separado) están aprobados de
momento (imaginad que les están entregando las notas).
NOTA: Los datos pueden ser aleatorios (nombres, edad, calificaciones, etc.) siempre y cuando
tengan sentido (edad no puede ser 80 en un estudiante o calificación ser 12).
129
}
@Override
public String toString() {
return "Nombre: " + nombre + ", Edad: " + edad + ", Sexo: " +
sexo;
}
}
import java.util.Random;
@Override
public boolean estaDisponible() {
// Los estudiantes tienen un 50% de probabilidad de hacer novillos
Random random = new Random();
return random.nextDouble() > 0.5;
}
@Override
public String toString() {
return "Estudiante - " + super.toString() + ", Calificación: " +
calificacion;
}
130
this.materia = materia;
}
@Override
public boolean estaDisponible() {
// Los profesores tienen un 20% de probabilidad de no estar
disponibles
Random random = new Random();
return random.nextDouble() > 0.2;
}
@Override
public String toString() {
return "Profesor - " + super.toString() + ", Materia: " + materia;
}
}
import java.util.ArrayList;
import java.util.List;
131
}
132
// Verificar si se puede dar clase
if (aula.puedeDarClase()) {
System.out.println("Se puede dar clase en el aula " +
aula.getIdentificador());
133
Vamos a hacer una baraja de cartas españolas orientado a objetos. Una carta tiene un número entre
1 y 12 (el 8 y el 9 no los incluimos) y un palo (espadas, bastos, oros y copas)
La baraja estará compuesta por un conjunto de cartas, 40 exactamente.
Las operaciones que podrá realizar la baraja son:
- barajar: cambia de posición todas las cartas aleatoriamente
- siguienteCarta: devuelve la siguiente carta que está en la baraja, cuando no haya más o se
haya llegado al final, se indica al usuario que no hay más cartas.
- cartasDisponibles: indica el número de cartas que aún puede repartir
- darCartas: dado un número de cartas que nos pidan, le devolveremos ese número de cartas
(piensa que puedes devolver). En caso de que haya menos cartas que las pedidas, no
devolveremos nada pero debemos indicárselo al usuario.
- cartasMonton: mostramos aquellas cartas que ya han salido, si no ha salido ninguna
indicárselo al usuario
- mostrarBaraja: muestra todas las cartas hasta el final. Es decir, si se saca una carta y luego se
llama al método, este no mostrara esa primera carta.
public enum Palo {
ESPADAS,
BASTOS,
OROS,
COPAS
}
public class Carta {
private int numero;
private Palo palo;
@Override
public String toString() {
return numero + " de " + palo;
}
}
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
134
private List<Carta> cartas;
private List<Carta> monton;
public Baraja() {
cartas = new ArrayList<>();
monton = new ArrayList<>();
135
}
}
return cartasRepartidas;
}
System.out.println("Cartas en el montón:");
for (Carta carta : monton) {
System.out.println(carta);
}
}
System.out.println("Cartas en la baraja:");
for (Carta carta : cartas) {
System.out.println(carta);
}
}
}
import java.util.List;
System.out.println("\nRepartiendo 5 cartas...");
List<Carta> cartasRepartidas = baraja.darCartas(5);
if (cartasRepartidas != null) {
System.out.println("Cartas repartidas:");
for (Carta carta : cartasRepartidas) {
136
System.out.println(carta);
}
}
137
Vamos a hacer el juego de la ruleta rusa en Java.
Como muchos sabéis, se trata de un número de jugadores que con un revolver con un sola bala en el
tambor se dispara en la cabeza.
Las clases a hacer son:
- Revolver:
o Atributos:
▪ posición actual (posición del tambor donde se dispara, puede que esté la
bala o no)
▪ posición bala (la posición del tambor donde se encuentra la bala)
Estas dos posiciones, se generaran aleatoriamente.
- Funciones:
o disparar(): devuelve true si la bala coincide con la posición actual
o siguienteBala(): cambia a la siguiente posición del tambor
o toString(): muestra información del revolver (posición actual y donde está la bala)
- Jugador:
o Atributos
▪ id (representa el número del jugador, empieza en 1)
▪ nombre (Empezara con Jugador más su ID, «Jugador 1» por ejemplo)
▪ vivo (indica si está vivo o no el jugador)
o Funciones:
▪ disparar(Revolver r): el jugador se apunta y se dispara, si la bala se dispara,
el jugador muere.
o Juego:
▪ Atributos:
• Jugadores (conjunto de Jugadores)
• Revolver
▪ Funciones:
• finJuego(): cuando un jugador muere, devuelve true
• ronda(): cada jugador se apunta y se dispara, se informara del
estado de la partida (El jugador se dispara, no ha muerto en esa
ronda, etc.)
El número de jugadores será decidido por el usuario, pero debe ser entre 1 y 6. Si no está en este
rango, por defecto será 6.
En cada turno uno de los jugadores, dispara el revólver, si este tiene la bala el jugador muere y el
juego termina.
Aunque no lo haya comentado, recuerda usar una clase ejecutable para probarlo.
import java.util.Random;
public Revolver() {
posicionActual = 1;
posicionBala = new Random().nextInt(6) + 1;
}
138
public boolean disparar() {
boolean resultado = (posicionActual == posicionBala);
siguienteBala();
return resultado;
}
}
import java.util.HashSet;
import java.util.Set;
139
public Juego(int numJugadores) {
if (numJugadores < 1 || numJugadores > 6) {
numJugadores = 6;
}
jugadores = new HashSet<>();
revolver = new Revolver();
for (int i = 1; i <= numJugadores; i++) {
jugadores.add(new Jugador(i));
}
}
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
140
Nos piden hacer un almacén, vamos a usar programación orientado a objetos.
En un almacén se guardan un conjunto de bebidas.
Estos productos son bebidas como agua mineral y bebidas azucaradas (coca-cola, fanta, etc). De los
productos nos interesa saber su identificador (cada uno tiene uno distinto), cantidad de litros, precio
y marca.
Si es agua mineral nos interesa saber también el origen (manantial tal sitio o donde sea).
Si es una bebida azucarada queremos saber el porcentaje que tiene de azúcar y si tiene o no alguna
promoción (si la tiene tendrá un descuento del 10% en el precio).
En el almacén iremos almacenado estas bebidas por estanterías (que son las columnas de la matriz).
- Calcular precio de todas las bebidas: calcula el precio total de todos los productos del
almacén.
- Calcular el precio total de una marca de bebida: dada una marca, calcular el precio total de
esas bebidas.
- Calcular el precio total de una estantería: dada una estantería (columna) calcular el precio
total de esas bebidas.
- Agregar producto: agrega un producto en la primera posición libre, si el identificador esta
repetido en alguno de las bebidas, no se agregará esa bebida.
- Eliminar un producto: dado un ID, eliminar el producto del almacén.
- Mostrar información: mostramos para cada bebida toda su información.
Puedes usar un main para probar las funcionalidades (añade productos, calcula precios, muestra
información, etc)
141
return precio;
}
@Override
public void mostrarInformacion() {
super.mostrarInformacion();
System.out.println("Porcentaje de Azúcar: " + porcentajeAzucar);
System.out.println("Tiene Promoción: " + tienePromocion);
}
}
142
this.origen = origen;
}
@Override
public void mostrarInformacion() {
super.mostrarInformacion();
System.out.println("Origen: " + origen);
}
}
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
143
public double calcularPrecioTotalEstanteria(int numEstanteria) {
double precioTotal = 0;
if (numEstanteria >= 0 && numEstanteria < estanterias.size()) {
List<Bebida> estanteria = estanterias.get(numEstanteria);
for (Bebida bebida : estanteria) {
precioTotal += bebida.getPrecio();
}
}
return precioTotal;
}
144
Comparator.comparing(Bebida::getMarca));
}
}
145