Universidad Carlos III de Madrid

Ingeniería de Telecomunicación

Enero-Mayo 2010 / January-May 2010

Orientación a Objetos y Herencia I

Lab Section1. Orientación a Objetos y Herencia (I)

TimeTOTAL: 90 min

Exercise Section1.1. Clases y Creación de Objetos

Time10 min

Objetivo

Practicar la declaracion de clases y la creacion de objetos.

Ejercicio

La clase Album va a representar la producción musical de un determinado cantante o grupo, que se caracteriza por los siguientes atributos:

  • title: El título del Album

  • author: El nombre del cantante o grupo

  • year: Año de publicación

  • elements: Número de cds o DVDs que incluye el Album

  • price: Precio recomendado de venta al público, sin incluir IVA

  • genre: Un caracter indicando el tipo de música (D: Dance, P: Pop, R: Rock, C: Classical, J: Jazz, O: Other)

  • isSpanish: True si el álbum es de un cantante español

Razona de qué tipo sería cada uno de los atributos y los modificadores de acceso. Luego escribe la clase Album en Java

Ahora programa una clase llamada AlbumTest, que únicamente contenga un método main y sirva para probar cómo funciona la clase Album. La clase de prueba debe realizar lo siguiente:

  • Crear dos objetos de tipo Album llamados album1 y album2

  • Dar valor a los atributos de ambos objetos con los datos de dos álbumes que te gusten

  • Imprimir los datos de cada álbum correctamente tabulados, como se presentan a continuación

               	Album:
    		Titulo:		Crazy Hits
    		Autor:		Crazy Frog
    		Año:		2005
    		Num:		1
    		Precio:		14.99
    		Género:		D
    		Español:	False
             

Solución

La solución se puede ver en los siguientes listados:

Album.java

public class Album { public String title; public String author; public int year; public int elements; public float price; public char genre; public boolean isSpanish; public void printAlbum() { System.out.println("Album:"); System.out.println("Title:\t\t"+title); System.out.println("Author:\t\t"+author); System.out.println("Year:\t\t"+year); System.out.println("Price:\t\t"+price+" euros"); System.out.println("Genre:\t\t"+genre); System.out.println("IsSpanish:\t"+isSpanish); System.out.println(""); // Might also be made by calling the access methods: // System.out.println("Title:\t\t"+getTitle()); // ... } }

AlbumTest.java

public class AlbumTest { public static void main(String[] args) { Album album1; Album album2; //Object creation album1 = new Album(); album2 = new Album(); //assigning value to the attributes album1.title="Crazy Hits"; album1.author="Crazy Frog"; album1.year=2005; album1.elements=1; album1.price=14.99f; album1.genre='D'; //album1.isSpanish=false; album2.title="Cien gaviotas dónde irán"; album2.author="Varios"; album2.year=2005; album2.elements=1; album2.price=14.99f; album2.genre='O'; album1.isSpanish=true; // printing information about album1 and album2 album1.printAlbum(); album2.printAlbum(); } }

Exercise Section1.2. Métodos de acceso

Time10 min

Objetivo

Practicar la encapsulación de objetos y el acceso mediante métodos públicos a atributos privados.

Ejercicio

Añade al programa métodos para rellenar y leer todos los campos de la clase. En el caso del atributo genre antes de asignar valor al atributo, comprueba que el caracter utilizado es un caracter válido.

Modifica el main de la clase AlbumTest para que utilice los metodos de acceso definidos. Comprueba que imprime el resultado correcto.

Solución

La solución se puede ver en el siguiente listado:

Album.java

public class Album { private String title; private String author; private int year; private int elements; private float price; private char genre; private boolean isSpanish; public void setTitle(String title) { this.title = title; } public void setAuthor(String author) { this.author = author; } public void setYear(int year) { this.year = year; } public void setElements(int elements) { this.elements = elements; } public void setPrice(float price) { this.price = price; } public void setGenre(char genre) { switch(genre){ case 'D' : case 'P' : case 'R' : case 'C' : case 'J' : case 'O' : this.genre = genre; break; default : System.out.println("The genre is not valid use one of the following characters: \n DANCE: D \n POP: P \n ROCK: R \n CLASSICAL: C \n JAZZ: J \n OTHER: O"); } } public void setIsSpanish(boolean isSpanish) { this.isSpanish = isSpanish; } public String getTitle() { return title; } public String getAuthor() { return author; } public int getYear() { return year; } public int getElements() { return elements; } public float getPrice() { return price; } public char getGenre() { return genre; } public boolean getIsSpanish() { return isSpanish; } public void printAlbum() { System.out.println("Album:"); System.out.println("Title:\t\t"+title); System.out.println("Author:\t\t"+author); System.out.println("Year:\t\t"+year); System.out.println("Price:\t\t"+price+" euros"); System.out.println("Genre:\t\t"+genre); System.out.println("IsSpanish:\t"+isSpanish); System.out.println(""); // Might also be made by calling the access methods: // System.out.println("Title:\t\t"+getTitle()); // ... } }

AlbumTest.java

public class AlbumTest { public static void main(String[] args) { Album album1; Album album2; //Object creation album1 = new Album(); album2 = new Album(); //assigning value to the attributes album1.setTitle("Crazy Hits"); album1.setAuthor("Crazy Frog"); album1.setYear(2005); album1.setElements(1); album1.setPrice(14.99f); album1.setGenre('D'); album1.setIsSpanish(false); album2.setTitle("Cien gaviotas dónde irán"); album2.setAuthor("Varios"); album2.setYear(2005); album2.setElements(1); album2.setPrice(14.99f); album2.setGenre('O'); album2.setIsSpanish(true); // printing information about album1 and album2 album1.printAlbum(); album2.printAlbum(); } }

Exercise Section1.3. Operadores

Time10 min

Objetivo

Practicar el uso de operadores aritméticos.

Ejercicio

Modifica AlbumTest.java para que calcule el precio total de los dos álbumes e imprima un mensaje en pantalla como este:

	  Precio total (sin IVA):	29.98 euros
	  Precio total (con 16% IVA):	34.7768 euros
	

Solución

La solución se puede ver en el siguiente listado:


AlbumTest.java

public class AlbumTest { public static void main(String[] args) { Album album1; Album album2; //Object creation album1 = new Album(); album2 = new Album(); //assigning value to the attributes album1.setTitle("Crazy Hits"); album1.setAuthor("Crazy Frog"); album1.setYear(2005); album1.setElements(1); album1.setPrice(14.99f); album1.setGenre('D'); album1.setIsSpanish(false); album2.setTitle("Cien gaviotas dónde irán"); album2.setAuthor("Varios"); album2.setYear(2005); album2.setElements(1); album2.setPrice(14.99f); album2.setGenre('O'); album2.setIsSpanish(true); // printing information about album1 and album2 album1.printAlbum(); album2.printAlbum(); System.out.println("Total price (without IVA): "+(album1.getPrice()+album2.getPrice())); System.out.println("Total price (with 16% IVA): "+(1.16*(album1.getPrice()+album2.getPrice()))); } }

Exercise Section1.4. Constructores

Time10 min

Objetivo

Practicar el uso de constructores y su relación con los métodos de acceso.

Ejercicio

Hasta ahora no hemos escrito ningún constructor para la clase Album. Después de crear un objeto usando el constructor por defecto se llama a los métodos de acceso setTitle o setAuthor para dar valor a los atributos del objeto creado. Esto tiene el inconveniente de que si nos olvidamos de rellenar algún atributo pueden quedar objetos Album con valores inconsistentes. Para solucionarlo, escribe un constructor para la clase Album que de valor a todos los atributos del objeto. Para hacerlo debes pasarle al constructor una lista de parámetros para rellenar todos los atributos del objeto. Esto nos permitirá llamar al constructor de la siguiente forma para crear un objeto:

	new Album("Crazy Hits","Crazy Frog",2005,1,14.99,'D',false);
	

Solución

La solución se puede ver en el siguiente listado:

Album.java

public class Album { private String title; private String author; private int year; private int elements; private float price; private char genre; private boolean isSpanish; public Album(String title, String author, int year, int elements, float price, char genre, boolean isSpanish) { setTitle(title); setAuthor(author); setYear(year); setElements(elements); setPrice(price); setGenre(genre); setIsSpanish(isSpanish); // In some cases, when there is no verification of data before making the assignment // you can asign value directly in the constructor instead of calling the Set method // this.title = title; // ... } public void setTitle(String title) { this.title = title; } public void setAuthor(String author) { this.author = author; } public void setYear(int year) { this.year = year; } public void setElements(int elements) { this.elements = elements; } public void setPrice(float price) { this.price = price; } public void setGenre(char genre) { switch(genre){ case 'D' : case 'P' : case 'R' : case 'C' : case 'J' : case 'O' : this.genre = genre; break; default : System.out.println("The genre is not valid use one of the following characters: \n DANCE: D \n POP: P \n ROCK: R \n CLASSICAL: C \n JAZZ: J \n OTHER: O"); } } public void setIsSpanish(boolean isSpanish) { this.isSpanish = isSpanish; } public String getTitle() { return title; } public String getAuthor() { return author; } public int getYear() { return year; } public int getElements() { return elements; } public float getPrice() { return price; } public char getGenre() { return genre; } public boolean getIsSpanish() { return isSpanish; } public void printAlbum() { System.out.println("Album:"); System.out.println("Title:\t\t"+title); System.out.println("Author:\t\t"+author); System.out.println("Year:\t\t"+year); System.out.println("Price:\t\t"+price+" euros"); System.out.println("Genre:\t\t"+genre); System.out.println("IsSpanish:\t"+isSpanish); System.out.println(""); // Might also be made by calling the access methods: // System.out.println("Title:\t\t"+getTitle()); // ... } }

AlbumTest.java

public class AlbumTest { public static void main(String[] args) { Album album1; Album album2; //Object creation album1 = new Album("Crazy Hits","Crazy Frog",2005, 1, 14.99f,'D',false); album2 = new Album("Cien gaviotas dónde irán","Varios",2005,1, 14.99f,'O',true); // printing information about album1 and album2 album1.printAlbum(); album2.printAlbum(); System.out.println("Total price (without IVA): "+(album1.getPrice()+album2.getPrice())); System.out.println("Total price (with 16% IVA): "+(1.16*(album1.getPrice()+album2.getPrice()))); } }

Exercise Section1.5. Constantes

Time10 min

Objetivo

Practicar el uso de las constantes y de la sentencia de selección switch.

Ejercicio

Crea una constante para identificar cada uno de los distintos generos musicales y asígnales el valor indicando en el enunciado por ejemplo ROCK='R'. Modifica el método setGenre para que utilice estas constantes para realizar las comprobaciones necesarias y asignar valor al atributo genre

Solución

La solución se puede ver en el siguiente listado:

Album.java

public class Album { public static final char DANCE = 'D'; public static final char POP = 'P'; public static final char ROCK = 'R'; public static final char CLASSICAL= 'C'; public static final char JAZZ= 'J'; public static final char OTHER= 'O'; private String title; private String author; private int year; private int elements; private float price; private char genre; private boolean isSpanish; public Album(String title, String author, int year, int elements, float price, char genre, boolean isSpanish) { setTitle(title); setAuthor(author); setYear(year); setElements(elements); setPrice(price); setGenre(genre); setIsSpanish(isSpanish); // In some cases, when there is no verification of data before making the assignment // you can asign value directly in the constructor instead of calling the Set method // this.title = title; // ... } public void setTitle(String title) { this.title = title; } public void setAuthor(String author) { this.author = author; } public void setYear(int year) { this.year = year; } public void setElements(int elements) { this.elements = elements; } public void setPrice(float price) { this.price = price; } public void setGenre(char genre) { switch(genre){ case DANCE : case POP : case ROCK : case CLASSICAL: case JAZZ: case OTHER: this.genre = genre; break; default : System.out.println("The genre is not valid use one of the following characters: \n DANCE: D \n POP: P \n ROCK: R \n CLASSICAL: C \n JAZZ: J \n OTHER: O"); } } public void setIsSpanish(boolean isSpanish) { this.isSpanish = isSpanish; } public String getTitle() { return title; } public String getAuthor() { return author; } public int getYear() { return year; } public int getElements() { return elements; } public float getPrice() { return price; } public char getGenre() { return genre; } public boolean getIsSpanish() { return isSpanish; } public void printAlbum() { System.out.println("Album:"); System.out.println("Title:\t\t"+title); System.out.println("Author:\t\t"+author); System.out.println("Year:\t\t"+year); System.out.println("Price:\t\t"+price+" euros"); System.out.println("Genre:\t\t"+genre); System.out.println("IsSpanish:\t"+isSpanish); System.out.println(""); // Might also be made by calling the access methods: // System.out.println("Title:\t\t"+getTitle()); // ... } }

Exercise Section1.6. Tipos de referencia

Time15 min

Objetivo

Practicar el uso de las clases como tipos de referencia en la declaración de atributos

Ejercicio

El precio de un álbum es algo que puede cambiar en el tiempo dependiendo de nuestras políticas de precios, ofertas de temporada, etc. vamos a cambiar el atributo price para gestionar mejor estos cambios. En lugar de utilizar un tipo basico (double) para representarlo vamos a crear un tipo específico de datos que llamaremos Rate (tarifa). De este modo podemos declarar una clase Rate y encapsular en ella un conjunto de datos y comportamientos específicos que nos permitirán calcular dicho precio.

Crea una clase Rate que contenga la siguiente información:

  • 3 constantes numéricas para los diferentes tipos de tarifa (NORMAL=0, REDUCED=1, INCREASED=2)

  • Un atributo llamado base para representar el precio sin ofertas ni recargos

  • Un atributo plus que indica la cantidad que hay que sumar o restar al precio básico dependiendo de que se trate de una tarifa aumentada o reducida

  • Un método getPrice que calcule el precio según la siguiente fórmula y devuelva su val:

    • Precio normal = base*num

    • Precio rebajado (ejemplo oferta de temporada) = base*num*(1-plus)

    • Precio aumentado (ejemplo edición especial) = base*num*(1+plus)

Solución

La solución se puede ver en el siguiente listado:

Album.java

public class Album { public static final char DANCE = 'D'; public static final char POP = 'P'; public static final char ROCK = 'R'; public static final char CLASSICAL= 'C'; public static final char JAZZ= 'J'; public static final char OTHER= 'O'; private String title; private String author; private int year; private int elements; private Rate rate; private char genre; private boolean isSpanish; public Album(String title, String author, int year, int elements, float price, char genre, boolean isSpanish) { setTitle(title); setAuthor(author); setYear(year); setElements(elements); setPrice(price); setGenre(genre); setIsSpanish(isSpanish); // In some cases, when there is no verification of data before making the assignment // you can asign value directly in the constructor instead of calling the Set method // this.title = title; // ... } public Album(String title, String author, int year, int elements, float price, float plus,char genre, boolean isSpanish) { setTitle(title); setAuthor(author); setYear(year); setElements(elements); setPrice(price, plus); setGenre(genre); setIsSpanish(isSpanish); // In some cases, when there is no verification of data before making the assignment // you can asign value directly in the constructor instead of calling the Set method // this.title = title; // ... } public Album(String title, String author, int year, int elements, float price, float plus,int rateType, char genre, boolean isSpanish) { setTitle(title); setAuthor(author); setYear(year); setElements(elements); setPrice(price, plus, rateType); setGenre(genre); setIsSpanish(isSpanish); // In some cases, when there is no verification of data before making the assignment // you can asign value directly in the constructor instead of calling the Set method // this.title = title; // ... } public void setTitle(String title) { this.title = title; } public void setAuthor(String author) { this.author = author; } public void setYear(int year) { this.year = year; } public void setElements(int elements) { this.elements = elements; } public void setPrice(float base){ rate = new Rate(base); } public void setPrice(float base, float plus) { rate = new Rate(base, plus); } public void setPrice(float base, float plus, int rateType) { rate = new Rate(base, plus, rateType); } public void setGenre(char genre) { switch(genre){ case DANCE : case POP : case ROCK : case CLASSICAL: case JAZZ: case OTHER: this.genre = genre; break; default : System.out.println("The genre is not valid use one of the following characters: \n DANCE: D \n POP: P \n ROCK: R \n CLASSICAL: C \n JAZZ: J \n OTHER: O"); } } public void setIsSpanish(boolean isSpanish) { this.isSpanish = isSpanish; } public String getTitle() { return title; } public String getAuthor() { return author; } public int getYear() { return year; } public int getElements() { return elements; } public float getPrice() { float resultado=0; resultado = (rate.getPrice())*elements; return resultado; } public char getGenre() { return genre; } public boolean getIsSpanish() { return isSpanish; } public int getRateType(){ int resultado = rate.getRateType(); return resultado; } public void printAlbum() { System.out.println("Album:"); System.out.println("Title:\t\t"+title); System.out.println("Author:\t\t"+author); System.out.println("Year:\t\t"+year); System.out.println("Price:\t\t"+getPrice()+" euros"); System.out.println("Genre:\t\t"+genre); System.out.println("IsSpanish:\t"+isSpanish); System.out.println("Rate type:\t"+getRateType()); System.out.println(""); // Might also be made by calling the access methods: // System.out.println("Title:\t\t"+getTitle()); // ... } }

AlbumTest.java

public class AlbumTest { public static void main(String[] args) { Album album1; Album album2; Album album3; //Object creation album1 = new Album("Crazy Hits","Crazy Frog",2005, 1, 14.99f,0.1f, 1,'D',false); album2 = new Album("Cien gaviotas dónde irán","Varios",2005,1, 14.99f,0.1f,2,'O',true); album3 = new Album("Prueba","Varios",2005,1, 14.99f,0.1f,0,'O',true); // printing information about album1 and album2 album1.printAlbum(); album2.printAlbum(); album3.printAlbum(); System.out.println("Total price (without IVA): "+(album1.getPrice()+album2.getPrice()+album3.getPrice())); System.out.println("Total price (with 16% IVA): "+(1.16*(album1.getPrice()+album2.getPrice()+album3.getPrice()))); } }

Rate.java

public class Rate{ private static final int NORMAL = 0; private static final int REDUCED = 1; private static final int INCREASED = 2; private int rateType; private float base; private float plus; public Rate(float base){ this.base = base; this.plus = 10; setRateType(0); } public Rate(float base, float plus){ this.base = base; this.plus = plus; setRateType(0); } public Rate(float base, float plus, int rateType){ this.base = base; this.plus = plus; setRateType(rateType); } public void setRateType(int rateType){ if (rateType==NORMAL || rateType == REDUCED || rateType == INCREASED){ this.rateType=rateType; }else{ this.rateType=-1; System.out.println("The rate type is not valid use one of the following values: \n NORMAL: 0 \n REDUCED: 1 \n INCREASED: 2 \n "); } } public float getPrice(){ float resultado = 0; switch(rateType){ case NORMAL: resultado = base; break; case REDUCED: resultado = base*(1+plus); break; case INCREASED: resultado = base*(1-plus); break; } return resultado; } public int getRateType(){ return rateType; } }

Exercise Section1.7. Herencia

Time15 min

Objetivo

Repasar los conceptos básicos de herencia y la sintaxis de java para crear clases derivadas.

Ejercicio

Como estamos trabajando con tres tipos distintos de tarifa, podemos mejorar nuestra implementación creando a partir de la clase Rate tres clases derivadas, una por cada tipo de tarifa. Para ello mantén los atributos comunes en la clase padre y crea un método especializado para calcular el precio correspondiente en cada una de las clases hijas.

Solución

La solución se puede ver en el siguiente listado:

Rate.java

public class Rate{ public static final int NORMAL = 0; public static final int REDUCED = 1; public static final int INCREASED = 2; private int rateType; private float base; private float plus; public Rate(float base){ this.base = base; this.plus = 10; setRateType(0); } public Rate(float base, float plus){ this.base = base; this.plus = plus; setRateType(0); } public Rate(float base, float plus, int rateType){ this.base = base; this.plus = plus; setRateType(rateType); } public void setRateType(int rateType){ if (rateType == NORMAL || rateType == REDUCED || rateType == INCREASED){ this.rateType=rateType; }else{ this.rateType=-1; System.out.println("The rate type is not valid use one of the following values: \n NORMAL: 0 \n REDUCED: 1 \n INCREASED: 2 \n "); } } public float getBase(){ return base; } public float getPlus(){ return plus; } public int getRateType(){ return rateType; } } //end of class

NormalRate.java

public class NormalRate extends Rate{ public NormalRate(float base){ super(base,10,0); } public NormalRate(float base, float plus){ super(base,plus,0); } public float getPrice(){ float resultado = 0; resultado = getBase(); return resultado; } }

ReducedRate.java

public class ReducedRate extends Rate{ public ReducedRate(float base){ super(base,10,1); } public ReducedRate(float base, float plus){ super(base,plus,1); } public float getPrice(){ float resultado = 0; resultado = getBase()*(1-getPlus()); return resultado; } }

IncreasedRate.java

public class IncreasedRate extends Rate{ public IncreasedRate(float base){ super(base,10,2); } public IncreasedRate(float base, float plus){ super(base,plus,2); } public float getPrice(){ float resultado = 0; resultado = getBase()*(1+getPlus()); return resultado; } }

Album.java

//herencia public class Album { public static final char DANCE = 'D'; public static final char POP = 'P'; public static final char ROCK = 'R'; public static final char CLASSICAL= 'C'; public static final char JAZZ= 'J'; public static final char OTHER= 'O'; private String title; private String author; private int year; private int elements; private Rate rate; // using a class as Reference Type instead of a basic type double price private char genre; private boolean isSpanish; public Album(String title, String author, int year, int elements, float price, char genre, boolean isSpanish) { setTitle(title); setAuthor(author); setYear(year); setElements(elements); setPrice(price); setGenre(genre); setIsSpanish(isSpanish); // In some cases, when there is no verification of data before making the assignment // you can asign value directly in the constructor instead of calling the Set method // this.title = title; // ... } public Album(String title, String author, int year, int elements, float price, float plus,char genre, boolean isSpanish) { setTitle(title); setAuthor(author); setYear(year); setElements(elements); setPrice(price, plus); setGenre(genre); setIsSpanish(isSpanish); // In some cases, when there is no verification of data before making the assignment // you can asign value directly in the constructor instead of calling the Set method // this.title = title; // ... } public Album(String title, String author, int year, int elements, float price, float plus,int rateType, char genre, boolean isSpanish) { setTitle(title); setAuthor(author); setYear(year); setElements(elements); setPrice(price, plus, rateType); setGenre(genre); setIsSpanish(isSpanish); // In some cases, when there is no verification of data before making the assignment // you can asign value directly in the constructor instead of calling the Set method // this.title = title; // ... } public void setTitle(String title) { this.title = title; } public void setAuthor(String author) { this.author = author; } public void setYear(int year) { this.year = year; } public void setElements(int elements) { this.elements = elements; } public void setPrice(float base){ rate = new NormalRate(base); } public void setPrice(float base, float plus) { rate = new NormalRate(base, plus); } public void setPrice(float base, float plus, int rateType) { switch(rateType){ case Rate.NORMAL: rate = new NormalRate(base, plus); break; case Rate.REDUCED: rate = new ReducedRate(base, plus); break; case Rate.INCREASED: rate = new IncreasedRate(base, plus); break; } } public void setGenre(char genre) { switch(genre){ case DANCE : case POP : case ROCK : case CLASSICAL: case JAZZ: case OTHER: this.genre = genre; break; default : System.out.println("The genre is not valid use one of the following characters: \n DANCE: D \n POP: P \n ROCK: R \n CLASSICAL: C \n JAZZ: J \n OTHER: O"); } } public void setIsSpanish(boolean isSpanish) { this.isSpanish = isSpanish; } public String getTitle() { return title; } public String getAuthor() { return author; } public int getYear() { return year; } public int getElements() { return elements; } //Example 1: using switch //This check is not necessary when we know dynamic binding //At runtime the runtime environment will know which is the //class to which we refer /* public float getPrice() { float resultado=0; int rateType= rate.getRateType(); switch (rateType){ case Rate.NORMAL: resultado = (((NormalRate)rate).getPrice())*elements; break; case Rate.REDUCED: resultado = (((ReducedRate)rate).getPrice())*elements; break; case Rate.INCREASED: resultado = (((IncreasedRate)rate).getPrice())*elements; break; } return resultado; } */ //Example 2: using the operator: instanceof //This check is not necessary when we know dynamic binding //At runtime the runtime environment will know which is the //class to which we refer public float getPrice() { float resultado=0; if (rate instanceof NormalRate){ resultado = (((NormalRate)rate).getPrice())*elements; }else if(rate instanceof ReducedRate){ resultado = (((ReducedRate)rate).getPrice())*elements; }else if(rate instanceof IncreasedRate){ resultado = (((IncreasedRate)rate).getPrice())*elements; }else{ //nunca pasa por aquí porque hemos hecho comprobación antes //este else se podría quitar System.out.println("Tipo de tarifa no válido"); } return resultado; } public char getGenre() { return genre; } public boolean getIsSpanish() { return isSpanish; } public int getRateType(){ int resultado = rate.getRateType(); return resultado; } public void printAlbum() { System.out.println("Album:"); System.out.println("Title:\t\t"+title); System.out.println("Author:\t\t"+author); System.out.println("Year:\t\t"+year); System.out.println("Price:\t\t"+getPrice()+" euros"); System.out.println("Genre:\t\t"+genre); System.out.println("IsSpanish:\t"+isSpanish); System.out.println("Rate type:\t"+getRateType()); System.out.println(""); // Might also be made by calling the access methods: // System.out.println("Title:\t\t"+getTitle()); // ... } }

AlbumTest.java

//herencia public class Album { public static final char DANCE = 'D'; public static final char POP = 'P'; public static final char ROCK = 'R'; public static final char CLASSICAL= 'C'; public static final char JAZZ= 'J'; public static final char OTHER= 'O'; private String title; private String author; private int year; private int elements; private Rate rate; // using a class as Reference Type instead of a basic type double price private char genre; private boolean isSpanish; public Album(String title, String author, int year, int elements, float price, char genre, boolean isSpanish) { setTitle(title); setAuthor(author); setYear(year); setElements(elements); setPrice(price); setGenre(genre); setIsSpanish(isSpanish); // In some cases, when there is no verification of data before making the assignment // you can asign value directly in the constructor instead of calling the Set method // this.title = title; // ... } public Album(String title, String author, int year, int elements, float price, float plus,char genre, boolean isSpanish) { setTitle(title); setAuthor(author); setYear(year); setElements(elements); setPrice(price, plus); setGenre(genre); setIsSpanish(isSpanish); // In some cases, when there is no verification of data before making the assignment // you can asign value directly in the constructor instead of calling the Set method // this.title = title; // ... } public Album(String title, String author, int year, int elements, float price, float plus,int rateType, char genre, boolean isSpanish) { setTitle(title); setAuthor(author); setYear(year); setElements(elements); setPrice(price, plus, rateType); setGenre(genre); setIsSpanish(isSpanish); // In some cases, when there is no verification of data before making the assignment // you can asign value directly in the constructor instead of calling the Set method // this.title = title; // ... } public void setTitle(String title) { this.title = title; } public void setAuthor(String author) { this.author = author; } public void setYear(int year) { this.year = year; } public void setElements(int elements) { this.elements = elements; } public void setPrice(float base){ rate = new NormalRate(base); } public void setPrice(float base, float plus) { rate = new NormalRate(base, plus); } public void setPrice(float base, float plus, int rateType) { switch(rateType){ case Rate.NORMAL: rate = new NormalRate(base, plus); break; case Rate.REDUCED: rate = new ReducedRate(base, plus); break; case Rate.INCREASED: rate = new IncreasedRate(base, plus); break; } } public void setGenre(char genre) { switch(genre){ case DANCE : case POP : case ROCK : case CLASSICAL: case JAZZ: case OTHER: this.genre = genre; break; default : System.out.println("The genre is not valid use one of the following characters: \n DANCE: D \n POP: P \n ROCK: R \n CLASSICAL: C \n JAZZ: J \n OTHER: O"); } } public void setIsSpanish(boolean isSpanish) { this.isSpanish = isSpanish; } public String getTitle() { return title; } public String getAuthor() { return author; } public int getYear() { return year; } public int getElements() { return elements; } //Example 1: using switch //This check is not necessary when we know dynamic binding //At runtime the runtime environment will know which is the //class to which we refer /* public float getPrice() { float resultado=0; int rateType= rate.getRateType(); switch (rateType){ case Rate.NORMAL: resultado = (((NormalRate)rate).getPrice())*elements; break; case Rate.REDUCED: resultado = (((ReducedRate)rate).getPrice())*elements; break; case Rate.INCREASED: resultado = (((IncreasedRate)rate).getPrice())*elements; break; } return resultado; } */ //Example 2: using the operator: instanceof //This check is not necessary when we know dynamic binding //At runtime the runtime environment will know which is the //class to which we refer public float getPrice() { float resultado=0; if (rate instanceof NormalRate){ resultado = (((NormalRate)rate).getPrice())*elements; }else if(rate instanceof ReducedRate){ resultado = (((ReducedRate)rate).getPrice())*elements; }else if(rate instanceof IncreasedRate){ resultado = (((IncreasedRate)rate).getPrice())*elements; }else{ //nunca pasa por aquí porque hemos hecho comprobación antes //este else se podría quitar System.out.println("Tipo de tarifa no válido"); } return resultado; } public char getGenre() { return genre; } public boolean getIsSpanish() { return isSpanish; } public int getRateType(){ int resultado = rate.getRateType(); return resultado; } public void printAlbum() { System.out.println("Album:"); System.out.println("Title:\t\t"+title); System.out.println("Author:\t\t"+author); System.out.println("Year:\t\t"+year); System.out.println("Price:\t\t"+getPrice()+" euros"); System.out.println("Genre:\t\t"+genre); System.out.println("IsSpanish:\t"+isSpanish); System.out.println("Rate type:\t"+getRateType()); System.out.println(""); // Might also be made by calling the access methods: // System.out.println("Title:\t\t"+getTitle()); // ... } }

Exercise Section1.8. Atributos estáticos

Time10 min

Objetivo

Repasar ejemplos sobre cómo y cuando usar los atributos estáticos.

Ejercicio

Queremos llevar la cuenta del número de álbumes creados por nuestra aplicación. Esta información se almacenará en un atributo llamado numAlbums

Queremos tener también un identificador único para cada uno de estos álbumes que almacenaremos en un atributo llamado albumID

Crea un método de acceso para cada uno de estos atributos getNumAlbums, getAlbumID

Teniendo en cuenta que el atributo numAlbums incrementa su valor cada vez que se crea un nuevo albúm ¿donde deberíamos actualizar el valor de este atributo?. ¿Tiene sentido crear un metodo setAlbums para darle valor?

Una forma para crear un identificador único para cada álbum es asignar al atributo albumID el valor del número de objetos existentes en el momento de su creación. ¿Sabrías cómo hacerlo? ¿Es necesario que el atributo albumID sea estático?

Solución

La solución se puede ver en el siguiente listado:

Album.java

public class Album { // This variables are constants. We use static (because it is the same // for every object) and final (because their value do not change) public static final char DANCE = 'D'; public static final char POP = 'P'; public static final char ROCK = 'R'; public static final char CLASSICAL= 'C'; public static final char JAZZ= 'J'; public static final char OTHER= 'O'; private String title; private String author; private int year; private int elements; private Rate rate; // using a class as Reference Type instead of a basic type double price private char genre; private boolean isSpanish; //this attribute is static (because it is the same for every object) // but it is no final (because its value change every time that a new // object is created) private static int numAlbums; //this attribute is not static because its value change in every obj private int albumID; public Album(String title, String author, int year, int elements, float price, char genre, boolean isSpanish) { setTitle(title); setAuthor(author); setYear(year); setElements(elements); setPrice(price); setGenre(genre); setIsSpanish(isSpanish); // In some cases, when there is no verification of data before making the assignment // you can asign value directly in the constructor instead of calling the Set method // this.title = title; // ... numAlbums++; //numAlbums is increased when a new object is created albumID= numAlbums; //The ID is the num of Albums when this object // have been created } public Album(String title, String author, int year, int elements, float price, float plus,char genre, boolean isSpanish) { setTitle(title); setAuthor(author); setYear(year); setElements(elements); setPrice(price, plus); setGenre(genre); setIsSpanish(isSpanish); // In some cases, when there is no verification of data before making the assignment // you can asign value directly in the constructor instead of calling the Set method // this.title = title; // ... numAlbums++; //numAlbums is increased when a new object is created albumID= numAlbums; //The ID is the num of Albums when this object // have been created } public Album(String title, String author, int year, int elements, float price, float plus,int rateType, char genre, boolean isSpanish) { setTitle(title); setAuthor(author); setYear(year); setElements(elements); setPrice(price, plus, rateType); setGenre(genre); setIsSpanish(isSpanish); // In some cases, when there is no verification of data before making the assignment // you can asign value directly in the constructor instead of calling the Set method // this.title = title; // ... numAlbums++; //numAlbums is increased when a new object is created albumID= numAlbums; //The ID is the num of Albums when this object // have been created } public void setTitle(String title) { this.title = title; } public void setAuthor(String author) { this.author = author; } public void setYear(int year) { this.year = year; } public void setElements(int elements) { this.elements = elements; } public void setPrice(float base){ rate = new NormalRate(base); } public void setPrice(float base, float plus) { rate = new NormalRate(base, plus); } public void setPrice(float base, float plus, int rateType) { switch(rateType){ case Rate.NORMAL: rate = new NormalRate(base, plus); break; case Rate.REDUCED: rate = new ReducedRate(base, plus); break; case Rate.INCREASED: rate = new IncreasedRate(base, plus); break; } } public void setGenre(char genre) { switch(genre){ case DANCE : case POP : case ROCK : case CLASSICAL: case JAZZ: case OTHER: this.genre = genre; break; default : System.out.println("The genre is not valid use one of the following characters: \n DANCE: D \n POP: P \n ROCK: R \n CLASSICAL: C \n JAZZ: J \n OTHER: O"); } } public void setIsSpanish(boolean isSpanish) { this.isSpanish = isSpanish; } public String getTitle() { return title; } public String getAuthor() { return author; } public int getYear() { return year; } public int getElements() { return elements; } public int getNumAlbums() { return numAlbums; } public int getAlbumID() { return albumID; } //Example 1: using switch //This check is not necessary when we know dynamic binding //At runtime the runtime environment will know which is the //class to which we refer public float getPrice() { float resultado=0; int rateType= rate.getRateType(); switch (rateType){ case Rate.NORMAL: resultado = (((NormalRate)rate).getPrice())*elements; break; case Rate.REDUCED: resultado = (((ReducedRate)rate).getPrice())*elements; break; case Rate.INCREASED: resultado = (((IncreasedRate)rate).getPrice())*elements; break; } return resultado; } */ //Example 2: using the operator: instanceof //This check is not necessary when we know dynamic binding //At runtime the runtime environment will know which is the //class to which we refer public float getPrice() { float resultado=0; if (rate instanceof NormalRate){ resultado = (((NormalRate)rate).getPrice())*elements; }else if(rate instanceof ReducedRate){ resultado = (((ReducedRate)rate).getPrice())*elements; }else if(rate instanceof IncreasedRate){ resultado = (((IncreasedRate)rate).getPrice())*elements; }else{ //nunca pasa por aquí porque hemos hecho comprobación antes //este else se podría quitar System.out.println("Tipo de tarifa no válido"); } return resultado; } public char getGenre() { return genre; } public boolean getIsSpanish() { return isSpanish; } public int getRateType(){ int resultado = rate.getRateType(); return resultado; } public void printAlbum() { System.out.println("NºAlbums:\t"+getNumAlbums()); System.out.println("Album ID:\t"+getAlbumID() ); System.out.println("Album:"); System.out.println("Title:\t\t"+title); System.out.println("Author:\t\t"+author); System.out.println("Year:\t\t"+year); System.out.println("Price:\t\t"+getPrice()+" euros"); System.out.println("Genre:\t\t"+genre); System.out.println("IsSpanish:\t"+isSpanish); System.out.println("Rate type:\t"+getRateType()); System.out.println(""); // Might also be made by calling the access methods: // System.out.println("Title:\t\t"+getTitle()); // ... } }

Homework Section2. Actividades para casa

Time120 min

Exercise Section2.1. Orientación a objetos y posible solución de cifrado I

Time30 min

Objetivo

Mediante este ejercicio pretendemos dar una visión de cómo la orientación a objetos nos permite reutilizar código siempre que la utilicemos correctamente.

Ejercicio

Supongamos que sabemos muy poco sobre teoría de cifrado de cadenas de caracteres. Digamos que conocemos de oídas varios algoritmos de cifrado, en su mayoría difíciles de implementar y uno, llamado Cesar, basado en una rotación en caracteres que es asequible de implementar por nosotros.

El algoritmo de César se basa en el orden de los caracteres en el abecedario. Para cada uno de los caracteres de la cadena de entrada se le suman N posiciones en el abecedario para encontrar el caracter por el que sustituir. Por ejemplo, si nos encontramos con el caracter 'A' y hay que sumar 3 posiciones, sustituiríamos la 'A' por la 'D' al cifrar ese caracter.

Implemente una solución orientada a objetos que me permita fácilmente en el futuro cambiar este algoritmo por otro mucho mejor. En esta solución no utilice ningún interface

Solución

La solución se puede ver en el siguiente listado:

// Notese como el unico comportamiento definido es simplemente el metodo main.
// ¿Se podria resolver de otra forma?
public abstract class Cifrado
{
	// Metodos que definen las posibilidades de  cifrado y descifrado.
	abstract public String cifrar(String cadena);
	abstract public String descifrar(String cadena);
	
	/**
	 * 
	 * @param args[0] cifrar/descifrar
	 * @param args[1] cadena a cifrar/descifrar
	 */
	public static void main(String[] args)
	{
		if (args.length !=2 )
			System.out.println("Uso: java Cifrado cifrar/descifrar [cadena]");
		else
		{
			CifradoCesar cc = new CifradoCesar();
			
			if (args[0].equals("cifrar"))
			{
				System.out.println(cc.cifrar(args[1]));
			}
			else if (args[0].equals("descifrar"))
			{
				System.out.println(cc.descifrar(args[1]));
			}
			else
			{
				System.out.println("Uso: java Cifrado cifrar/descifrar [cadena]");
			}
		}			
	} // main

} // Cifrado

// Implementacion especifica para un cifrado por rotacion de caracteres (cesar).
class CifradoCesar extends Cifrado
{
/** Numero de caracteres que rotamos */
	private static int NUM_CARACTERES_ROTAR = 10;
	
	/** Caracteres que pueden rotarse 
	 *  Podriamos cambiar el orden de las letras en el array
	 */
	private static char caracteres [] = 
	  {'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r',
		's','t','u','v','w','x','y','z','_','-','.','#','@','&','%','/','(','(','+',
		'A','B','C','D','E','F','G','H','I','J','K','0','1','2','3','4','5','6','7','8','9',
		'L','M','N','O','P','Q','R','S','T','U','W','X','Y','Z'};
	
	/**
	 * Ante un caracter dado nos devuelve la posición en el array.
	 * -1 sino se encuentra
	 * @param caracter
	 * @return
	 */
	private int buscarPosCaracter(char caracter)
	{
		int pos = -1;
		boolean encontrado = false;
		
		int i = 0;		
		while ( (!encontrado) && (i<caracteres.length) )
		{
			encontrado = (caracteres[i] == caracter);
			i++;
		}
		if (encontrado) 
			return (i-1);
		else 
			return pos;		
	} // buscarPosCaracter()
	
	/**
	 * Rota el caracter dado. 
	 * @param caracter caracter a rotar
	 * @return devuelve caracter rotado NUM_CARACTERES_ROTAR incrementando
	 */
	private char siguiente(char caracter)
	{		
		int pos = buscarPosCaracter(caracter);
		if (pos==-1)
			return caracter;
		else
		{
			int posCarCifrado = 0;
			int nuevaPos = pos + CifradoCesar.NUM_CARACTERES_ROTAR; 
			if (nuevaPos <= caracteres.length-1 )
				posCarCifrado = nuevaPos;
			else
				posCarCifrado = nuevaPos - caracteres.length; 
			
			return caracteres[posCarCifrado];
		}	
				
	} //siguiente()
	
	/**
	 * Rota el caracter dado. 
	 * @param caracter caracter a rotar
	 * @return devuelve caracter rotado NUM_CARACTERES_ROTAR decrementando
	 */
	private char anterior(char caracter)
	{
		int pos = buscarPosCaracter(caracter);
		if (pos==-1)
			return caracter;
		else
		{
			int posCarDescifrado = 0;
			int nuevaPos = pos - CifradoCesar.NUM_CARACTERES_ROTAR;
			if (nuevaPos >= 0 )
				posCarDescifrado = nuevaPos;
			else
				posCarDescifrado = caracteres.length - Math.abs(nuevaPos);
			return caracteres[posCarDescifrado];
		}		
	} // anterior()
	
	
	
	/**
	 * Permite cifrar una cadena mediante metodo Cesar basico. 
	 * Rotacion de caracteres. 
	 * @param cadena - Cadena a cifrar
	 * @return Cadena Cifrada
	 */
	public String cifrar(String cadena)
	{
		String cadenaCifrada = "";
		for (int i=0;i<cadena.length(); i++)
		{
			char car = cadena.charAt(i);
			car = siguiente(car);
			cadenaCifrada += car;
		}
		return cadenaCifrada;
	} // cifrar

	/**
	 * Permite descifrar una cadena mediante metodo Cesar basico. 
	 * Rotacion de caracteres. 
	 * @param cadena - Cadena a descifrar
	 * @return Cadena Descifrada
	 */
	public String descifrar(String cadena)
	{
		String cadenaDescifrada = "";
		for (int i=0;i<cadena.length(); i++)
		{
			char car = cadena.charAt(i);
			car = anterior(car);
			cadenaDescifrada += car;
		}
		return cadenaDescifrada;
	} // Descifrar
} // CifradoCesar
	  

Exercise Section2.2. Ejercicio para practicar con Fechas

Time45 min

En este ejercicio, vamos a crear una clase Fecha que nos permita trabajar con fechas. La clase debe almacenar una serie de atributos y debe definir una serie de métodos para realizar distintas operaciones básicas.

Antes de escribir código, piensa cuidadosamente el diseño de la clase Fecha.

  1. ¿Qué información debe almacenar cada objeto? Si un mismo dato puede almacenarse en distintos formatos, ¿usarías varios atributos para representar la misma información? ¿Por qué? ¿Qué formato crees que es más conveniente?

  2. ¿Qué constructores proporcionarías? Observa que los constructores tendrán que inicializar los datos internos del objeto.

  3. Piensa qué métodos crees que sería útil incluir.

  4. ¿Se te ocurre alguna información que sería conveniente incluir en la clase, común para todos los objetos Fecha?

Atributos de la clase

  1. Define los atributos de la clase Fecha.

Constructores de la clase.

La clase Fecha debe proporcionar dos constructores que inicialicen objetos de tipo Fecha:

  • Un constructor con tres enteros que indican el día, mes y año respectivamente.

  • Un constructor con tres cadenas de caracteres, que indican el día, el mes y el año.

  1. Programar dichos constructores, de acuerdo a las siguentes declaraciones:

    public Fecha(int dia, int mes, int año) {
        /* completar */
    }
    
    public Fecha(String dia, String mes, String año) {
        /* completar */
    }
    

Métodos de acceso de los atributos de la clase.

Los atributos del objeto conviene que se declaren de acceso privado para evitar errores en su modificación. Programa los métodos get y set necesarios para leer y modificar la información encapsulada en los objetos Fecha.

Método toString() de representación del objeto.

El método toString() tiene un significado especial en los objetos Java. Es el método que se utiliza para obtener una representación como cadena de texto de dicho objeto mostrando los valores de los atributos en un instante determinado.

  1. Programa el método toString() que tiene la siguiente declaración:

    public String toString() {
        /* completar */
    }
    

    Este método tiene que devolver una cadena de texto con la representación de una fecha, según el siguiente formato:

    día(Numero) de mes(Texto) de año(Numero)

    EJEMPLO : 6 de Agosto de 2007

Método compareTo() para comparar fechas.

El método compareTo() tiene un significado especial en los objetos Java. Es el método que se utiliza para comparar dos objetos.

LECTURA RECOMENDADA: Consulta el API del interfaz Comparable para comprobar qué valor debe devolver en los distintos casos: según si el objeto con el que comparamos el objeto actual es mayor, menor o igual que éste.

  1. Programa el método compareTo() que tiene la siguiente declaración:

    public int compareTo( Fecha fecha ) {
        /* ... */
    }
    

    El método compara la fecha almacenada en el objeto con la que se le pasa como parámetro, devolviendo como resultado:

    • -1 si la fecha contenida en el objeto actual es anterior a la que se pasa por parámetro.

    • 0 si son iguales.

    • 1 si la fecha contenida en el objeto actual es posterior a la que se pasa por parámetro.

Método auxiliar sobrecargado.

Recuerda que en una clase pueden aparecer varios métodos con el mismo nombre, siempre que tengan distintos parámetros de entrada. A esto se le denomina sobrecarga de métodos (en inglés, overload).

  1. Programa un método auxiliar que devuelve el número de días que tiene el mes que se le pasa como parámetro. Sobrecarga el método de modo que pueda utilizarse el número o el nombre del mes. Los métodos tienen las siguientes declaraciones:

    ... int getDiasMes(int mes) {
        /* completar */
    }
    
    ... int getDiasMes(String mes) {
        /* completar */
    }
    

Solución

Las soluciones del ejercicio se pueden ver en los siguientes listados:

	      import java.io.*;


public class Fecha {

   /**
    * Meses del año, ordenados, para facilitar la conversión de formato cadena a número y viceversa
    *
    * El número de mes coincidirá con la posición de su nombre en el array más 1
    * (porque las posiciones del array se numeran a partir de 0 pero los meses a partir de 1)
    */
   private static final String[] meses = { "enero", "febrero", "marzo", "abril", "mayo", 
      "junio", "julio", "agosto", "septiembre", "octubre", "noviembre", "diciembre" };


   // ATRIBUTOS
   
   private int dia;
   private int mes;
   private int agno;



   // CONSTRUCTORES
   
   public Fecha(int dia, int mes, int agno) throws FechaException {
      
      comprobarFecha( dia, mes, agno );
   
      this.dia = dia;
      this.mes = mes;
      this.agno = agno;
      
   }

   public Fecha(String sDia, String sNombreMes, String sAgno) throws FechaException {
      
   	// Convertir las cadenas del dia y el año en numeros
      try {
         dia = Integer.parseInt(sDia);
         agno = Integer.parseInt(sAgno);
         
      } catch ( NumberFormatException nfe ) {
         // Error al convertir una cadena en numero
         throw new FechaException( "Fecha incorrecta" );
      }
   
      mes = getMes( sNombreMes );
         
      // Comprobar ademas que los valores tienen sentido
      comprobarFecha( dia, mes, agno );
   
   }


   // ACCESORES
   
   public int getDia() {
      return dia;
   }

   public int getMes() {
      return mes;
   }

   public int getAgno() {
      return agno;
   }

   public void setDia(int dia) throws FechaException {
       comprobarFecha(dia, this.mes, this.agno);
       this.dia = dia;
   }

   public void setMes(int mes) throws FechaException {
       comprobarFecha(this.dia, mes, this.agno);
       this.mes = mes;
   }

   public void setAgno(int agno) throws FechaException {
       comprobarFecha(dia, mes, agno);
       this.agno = agno;
   }



   // METODOS
   
   public String toString() {
      return ( dia + " de " + meses[mes-1] + " de " + agno );
   }

   public int compareTo( Fecha fecha ) {

       int resultado = 0;
   
      if ( agno < fecha.getAgno() ) {
         resultado = -1;
      } 
      else if ( agno > fecha.getAgno() ) {
         resultado = 1;
      } 
      else {		// los años son iguales => comparar los meses
      
         if ( mes < fecha.getMes() ) {
            resultado = -1;
         } 
         else if (mes > fecha.getMes()) {
            resultado = 1;
         } 
         else {		// los meses también son iguales => comparar los dias
         
            if (dia < fecha.getDia()) {
               resultado = -1;
            } 
            else if (dia > fecha.getDia()) {
               resultado = 1;
            } 
            else {		// los dias también son iguales => misma fecha
               resultado = 0;
            }
         }
      }	

      return resultado;
   }


   // METODOS AUXILIARES
   


   /**
    * Devuelve el numero de dias de un mes dado
    */
   private int getDiasMes( int mes ) throws FechaException {
      
      int diasMes = 0;
      
      switch( mes ) {
      
         case 1:
         case 3:
         case 5:
         case 7:
         case 8:
         case 10:
         case 12:		// meses de 31 dias
            diasMes = 31;
            break;
      
         case 4:
         case 6:
         case 9:
         case 11:		// meses de 30 dias
            diasMes = 30;
            break;
      
         case 2:		// febrero
            if (esBisiesto( agno ))
               diasMes = 29;
            else
               diasMes = 28;
            break;
      		
         default:			// mes incorrecto
            throw new FechaException( "Mes incorrecto" );
      }
      
      return diasMes;
   	
   }


   /**
    * Devuelve el número de días de un mes dado
    */
   private int getDiasMes( String mes ) throws FechaException {
      return getDiasMes( getMes(mes) );
   }



   /**
    * Método auxiliar para calcular el número de mes, dado su nombre.
    * Devuelve -1 si el texto introducido no se corresponde con un mes.
    */
   private static int getMes( String sNombreMes ) throws FechaException {

      // Hallar el numero de mes
      int mes = -1;
      for (int i=0; i<meses.length && mes==0; i++) {
         if ( meses[i].equalsIgnoreCase( sNombreMes ) ) {
            // El mes coincide con el elemento actual del array
            mes = i+1;  
         }
      }
      /*
      if (mes == 0) {
         // si el mes introducido no coincide con ninguno de los almacenados 
         // en el array es que hay un error
         throw new FechaException( "Mes incorrecto" );
      }
      */
      
      return mes;
      
   }
   

   /**
    * Método auxiliar para comprobar que una fecha es correcta
    */
   private void comprobarFecha( int dia, int mes, int agno ) throws FechaException {

      // Comprobar que el dia es correcto
      // (El método getDiasMes ya comprueba que el mes sea correcto)
      if ( (dia <= 0) || (dia > getDiasMes(mes)) ) {
         throw new FechaException( "Dia incorrecto" ); 
      }
   	
   }


	/**
	 * Comprueba si un año es bisiesto
	 * Son bisiestos los años múltiplos de 4 que no sean múltiplos de 100
	 * y los múltiplos de 400
	 */
   private boolean esBisiesto( int agno ) {
      return (( ((agno % 4) == 0) && ((agno % 100) != 0) ) 
             || ((agno % 400) == 0) );
   }

   public static Fecha leerFecha() {

       BufferedReader entrada = new BufferedReader( new InputStreamReader( System.in ) );
       Fecha fecha = null;
       String sDia, sMes, sAgno;	// Variables para almacenar la entrada del usuario
       int iMes;

       boolean correcto = false;

       do {

	   try {

	       System.out.print("Introduzca dia: ");
	       System.out.flush();
	       sDia = entrada.readLine();
	       System.out.print("Introduzca mes: ");
	       System.out.flush();
	       sMes = entrada.readLine();
	       System.out.print("Introduzca año: ");
	       System.out.flush();
	       sAgno = entrada.readLine();

	       // Probamos si el usuario ha introducido el nombre del mes
	       iMes = getMes(sMes);         

	       if (iMes == -1) {
		   // Probamos si el usuario ha introducido el número de mes
		   iMes = Integer.parseInt(sMes);
	       }

	       fecha = new Fecha( sDia, meses[iMes], sAgno );
	       correcto = true;

	   } catch (Exception ex) {
	       correcto = false;
	   }


       } while (!correcto);


       return fecha;
   }

    public Fecha fechaMasReciente(Fecha[] fechas) {

	Fecha fecha = null;

	if ((fechas != null) && (fechas.length > 0)) {
	    
	    int max = 0;
	    for (int i=1; i<fechas.length; i++) {
		if (fechas[max].compareTo(fechas[i]) < 0) {
		    max = i;
		}
	    }
	    fecha = fechas[max];
	}

	return fecha;
	
    }

}

	  
	      /**
 * La excepción FechaException se lanza cuando se intenta
 * generar un objeto Fecha con datos incorrectos
 */
public class FechaException extends Exception {

	/** 
	 * Constructor sin parámetros,
	 * genera una excepción sin mensaje explicativo específico.
	 */
	public FechaException() {
		super();	// se llama al constructor de la clase padre Exception
	}

	/** 
	 * Constructor con parámetros,
	 * genera una excepción con un mensaje explicativo del error.
	 *
	 * @param mensaje explicativo del error que ha generado la excepción
	 */
	public FechaException( String mensaje ) {
		super( mensaje );	// se llama al constructor de la clase padre Exception
	}
}
	  

Exercise Section2.3. Puntos y Figuras Geométricas (I)

Time45 min

La clase Punto.

Un punto en el plano se puede representar mediante un par de coordenadas x e y, ambas tomando valores en el conjunto de los números reales. En Java podemos representar un punto en el plano mediante una instancia de la siguiente clase:

public class Punto {
    private double x;
    private double y;

    /* métodos */
    /* a completar en el resto de apartados */
}
Constructor de la clase.

En Java, el constructor de la clases sirve para inicializar los valores de los atributos a la hora de instanciarse un objeto de la misma en tiempo de ejecución.

  1. Programa un constructor para la clase Punto. Debe recibir como parámetros las dos coordenadas del punto.

Método para representar un objeto en formato de texto.

El método toString() tiene un significado especial en los objetos Java. Es el método que se utiliza para obtener una representación como cadena de texto de dicho objeto.

  1. Programa el método toString() para que devuelva una cadena de texto con la representación del punto con el siguiente formato:

    (x, y)

    donde x e y deben ser reemplazados por sus respectivos valores. El prototipo de dicha función se especifica a continuación:

    public String toString() {
        /* ... */
    }

    Pista

    Recuerda que el operador "+", aplicado a cadenas de texto, permite concatenarlas. Si se concatenan números a cadenas, Java los convierte automáticamente a cadenas de texto para realizar la concatenación.

El método main.

Ahora debes crear clase para probar el código anterior.

  1. Programa una clase llamada Prueba que tenga un método main. Este método debe recibir como argumentos de línea de comandos las coordenadas x e y, crear un nuevo objeto Punto con dichas coordenadas e imprimir en su salida estándar (la pantalla en este caso) la representación textual de dicho objeto.

    El programa debe comprobar que el número de argumentos de línea de comandos recibido sea el correcto.

    Pista

    El método parseDouble de la clase Double (consulta su API pinchando en el enlace) transforma una cadena de texto a un tipo primitivo double.

Los métodos de acceso.

Es habitual que los atributos de un objeto se declaren como privados (private) para evitar accesos incontrolados desde otros puntos del código. Si es necesario que desde el exterior se acceda a estos atributos, se proporcionan métodos cuyo nombre empieza, por convenio, con las cadenas "get" (acceso en lectura) y "set" (acceso en escritura).

  1. Programa en la clase Punto los siguientes métodos, que devuelven el valor de las coordenadas x e y. El prototipo de dichos métodos se muestra a continuación:

    public double getX() {
        /* ... */
    }
    
    public double getY() {
        /* ... */
    }
  2. Modifica el código de tu clase Prueba para comprobar que su comportamiento es correcto.

Cálculo de distancias.

En este apartado vamos a implementar dos métodos auxiliares que nos permitirán calcular distancias entre puntos.

  1. Programa un método de la clase Punto que devuelva la distancia del punto al origen de coordenadas. El prototipo del método se muestra a continuación:

    public double distanciaAlOrigen() {
    /*  completar */
    }

    Modifica el código de tu clase Prueba para comprobar que su comportamiento es correcto.

  2. Programa un método en Punto que devuelva la distancia entre el punto representado por la instancia actual del objeto y otra instancia de Punto que se recibe como parámetro. El prototipo del método se muestra a continuación:

    public double calcularDistancia(Punto otroPunto) {
    /* completar */
    }

    Modifica el código de tu clase Prueba para comprobar que su comportamiento es correcto.

Cálculo de cuadrante.
  1. Programa un método de la clase Punto que devuelva el cuadrante en el que se encuentra el punto.El prototipo del método se muestra a continuación:

    • Devuelve 0 si está en el origen de coordenadas o sobre alguno de los ejes.

    • Devuelve 1 si está en el primer cuadrante (x e y positivos).

    • Devuelve 2 si está en el segundo cuadrante (x negativo e y positivo).

    • Devuelve 3 si está en el tercer cuadrante (x e y negativos).

    • Devuelve 4 si está en el cuarto cuadrante (x positivo e y negativo).

    El prototipo del método se muestra a continuación:

    public int calcularCuadrante() {
      /* completar */
    }

    Modifica el código de tu clase de prueba para comprobar que su comportamiento es correcto.

Cálculo del punto más cercano.

En este apartado tienes que basarte en los métodos de los apartados anteriores.

  1. Programa un método en Punto que reciba como parámetro un array de objetos de la clase Punto y devuelva una referencia al objeto de dicho array que esté más cercano al punto actual.El prototipo del método se muestra a continuación:

    public Punto calcularMasCercano(Punto[] otrosPuntos) {
      /* completar */
    }

    Modifica el código de tu clase Prueba para comprobar que su comportamiento es correcto.

La clase Triangulo.

Un triángulo está plenamente definido por sus tres vértices. Estos vértices se pueden representar como objetos de la clase Punto.

Declaración de la clase.

En este apartado tienes que basarte en los métodos de los apartados anteriores.

  1. Programa la clase Triangulo con sus atributos y un constructor que reciba como parámetro tres puntos del plano.

Longitud de los lados.

Programa una clase para probarlo.

  1. Programa el método calcularLongitudLados() de la clase Triangulo, que debe devolver un array de tres posiciones, cada una de las cuales debe ser la longitud de uno de los lados del triángulo. El prototipo del método se muestra a continuación:

    public double[] calcularLongitudLados() {
        /* completar... */
    }

Soluciones

Las soluciones del ejercicio se pueden ver en el siguiente listado:

// Clase Punto
// (c) 2008 IT

public class Punto {
  private double x;
  private double y;

  // Constructor
  public Punto(double x, double y) {
    this.x = x;
    this.y = y;
  }

  // toString: devuelve una cadena con la representación del objeto
  public String toString() {
    // el signo + para cadenas no significa "suma matemática" sino concatenación
    return "("+x+", "+y+")";
  }

  // devuelve la distancia al origen
  // NOTA: se podría haber hecho con la distancia a (0,0):
  //   return calcularDistancia(new Punto(0,0));
  public double distanciaAlOrigen() {
    return Math.sqrt(x*x+ y*y);
  }

  // métodos de acceso
  public double getX() {
    return x;
  }
  public double getY() {
   return y;
  }

  // devuelve la distancia a otro punto
  public double calcularDistancia(Punto otroPunto) {
    double x1;
    double y1;
    
    x1 = x-otroPunto.getX();
    y1 = y-otroPunto.getY();
    return Math.sqrt(x1*x1+y1*y1);
  }

  // devuelve el cuadrante
  public int calcularCuadrante() {
    if(x>0.0 && y>0.0)
      return 1;
    else if(x<0.0 && y>0.0)
      return 2;
    else if(x<0.0 && y<0.0)
      return 3;
    else if(x>0.0 && y<0.0)
      return 4;
    else // (x==0.0 || y==0.0)
      return 0;
  }

  // calcula el punto más cercano del array
  public Punto calcularMasCercano(Punto[] otrosPuntos) {
    Punto puntoMasCercano = null;
    double distanciaMinima;
    double distancia;

    if(otrosPuntos!=null && otrosPuntos.length>0) {
      // me guardo el primero como más cercano
      distanciaMinima = calcularDistancia(otrosPuntos[0]);
      puntoMasCercano = otrosPuntos[0];

      for(int i=1; i<otrosPuntos.length; i++) {
        // si encuentro otro más cercano, lo sustituyo
        distancia = calcularDistancia(otrosPuntos[i]);
        if(distancia<distanciaMinima) {
          distancia = distanciaMinima;
          puntoMasCercano = otrosPuntos[i];
        }
      }
    }
    // devuelve el punto encontrado si el array contiene puntos, null en caso contrario
    return puntoMasCercano;
  }
}

	  
// Clase de prueba de Punto
// (c) 2007 IT
public class Prueba {

  public static void main(String args[]) {
    double x;
    double y;
    Punto p;
    Punto arrayPuntos[];

    // Comprobar el número de argumentos (el nombre de programa más las dos coordenadas)
    if(args.length!=2) {
      System.out.println("Uso:");
      System.out.println("  java Prueba x y");
      System.out.println("donde x e y son las cooordenadas de un punto en el espacio.");
      return;
    }

    // Manejador de excepciones
    try {
      x = Double.parseDouble(args[0]);
      y = Double.parseDouble(args[1]);
    } catch(Exception e) {
      System.out.println("Error en los argumentos de la línea de comandos.");
      return;
    }

    p = new Punto(x, y);
    System.out.println("El punto es "+p.toString());
    System.out.println("Su distancia al origen es "+p.distanciaAlOrigen());
    System.out.println("Su coordenada x es "+p.getX()+" y su coordenada y es "+p.getY());

    System.out.println("Está ubicado en el cuadrante "+p.calcularCuadrante());

    arrayPuntos = new Punto[5];
    arrayPuntos[0] = new Punto(1,1);
    arrayPuntos[1] = new Punto(5,3);
    arrayPuntos[2] = new Punto(10,10);
    arrayPuntos[3] = new Punto(-3,2);
    arrayPuntos[4] = new Punto(-4,-5);

    for(int i=0; i<arrayPuntos.length; i++)
      System.out.println("Su distancia al punto "+arrayPuntos[i].toString()+" es "+p.calcularDistancia(arrayPuntos[i]));

    System.out.println("El punto más cercano es "+p.calcularMasCercano(arrayPuntos));

  }
}
	  
// Clase de prueba de Triangulo
// (c) 2007 IT
public class PruebaTriangulo {

  public static void main(String args[]) {
    double x1;
    double y1;
    double x2;
    double y2;
    double x3;
    double y3;
    Punto v1;
    Punto v2;
    Punto v3;
    Triangulo t;
    double longitudLados[];

    // Comprobar el número de argumentos (el nombre de programa más las dos coordenadas)
    if(args.length!=6) {
      System.out.println("Uso:");
      System.out.println("  java PruebaTriangulo x1 y1 x2 y2 x3 y3");
      System.out.println("donde (xi,yi) son las cooordenadas de cada vértice del triángulo.");
      return;
    }

    // Manejador de excepciones
    try {
      x1 = Double.parseDouble(args[0]);
      y1 = Double.parseDouble(args[1]);
      x2 = Double.parseDouble(args[2]);
      y2 = Double.parseDouble(args[3]);
      x3 = Double.parseDouble(args[4]);
      y3 = Double.parseDouble(args[5]);
    } catch(Exception e) {
      System.out.println("Error en los argumentos de la línea de comandos.");
      return;
    }

    // Se crean los tres vértices y el triángulo
    v1 = new Punto(x1, y1);
    v2 = new Punto(x2, y2);
    v3 = new Punto(x3, y3);
    t = new Triangulo(v1, v2, v3);

    // Presentación de la información
    System.out.println("El triángulo tiene como vértices "+v1.toString()+", "+v2.toString()+", "+v3.toString());

    longitudLados = t.calcularLongitudLados();
    System.out.println("Sus lados miden "+longitudLados[0]+", "+longitudLados[1]+", "+longitudLados[2]);
  }
}
	  
// Clase Triangulo
// (c) 2007 IT
public class Triangulo {

  private Punto vertice1;
  private Punto vertice2;
  private Punto vertice3;

  public Triangulo(Punto vertice1, Punto vertice2, Punto vertice3) {
    this.vertice1 = vertice1;
    this.vertice2 = vertice2;
    this.vertice3 = vertice3;
  }

  public double[] calcularLongitudLados() {
    double longitudLados[] = new double[3];
 
    // longitud 1->2
    longitudLados[0] = vertice1.calcularDistancia(vertice2);  
    // longitud 2->3
    longitudLados[1] = vertice2.calcularDistancia(vertice3);
    // longitud 3->1
    longitudLados[2] = vertice3.calcularDistancia(vertice1);

    return longitudLados;
  }
}