Documenti di Didattica
Documenti di Professioni
Documenti di Cultura
Vimos en la Ficha anterior que la POO busca identificar actores o entidades en el dominio de un
problema, que sean capaces de ejecutar ciertas acciones. Los lenguajes orientados a objetos usan
clases para describir la forma y el comportamiento general de estos actores (que se llaman
genéricamente objetos). Una clase contiene definiciones de atributos (o variables o también campos)
que indican la forma de cada objeto. Y una clase también contiene métodos (o procesos) que indican el
comportamiento que puede desplegar cada objeto de esa clase.
La propiedad de la POO que permite que una clase agrupe atributos y métodos se llama
encapsulamiento, debido a que mediante esa agrupación una clase se asemeja a una cápsula de
software que permitirá crear objetos provistos cada uno de todos los elementos de datos y procesos
que necesitarán para desenvolverse en un programa.
En el ejemplo anterior, se crea un objeto de la clase Cuenta en el método main() que está en la
clase Principal. Por lo tanto, main() no tiene acceso libre al atributo numero del objeto a: la
instrucción a.numero = 201; no compilará. Note que si el atributo numero se hubiera declarado public
en la clase Cuenta, entonces no se produciría el error: un elemento público es accesible desde
cualquier método de cualquier clase.
Ing. Valerio Frittelli - 1
Ficha de Estudio: Gestión de Referencias y otros conceptos de Java
Así como un diagrama de flujo es una herramienta gráfica muy útil cuando un programador
quiere concentrarse en la lógica de un algoritmo sin entrar en los detalles formales de un lenguaje de
programación, existen otros modelos gráficos que permiten concentrarse en las relaciones que existen
entre las diversas clases de un proyecto y permitir que el programador tenga una visión general amplia
del proyecto, sin entrar en la complejidad de la programación profunda de cada clase. Esos dieños
gráficos se construyen en base a reglas y simbologías propias de un lenguaje de modelado llamado
Lenguaje Unificado de Modelado (o UML por sus iniciales en inglés: Unified Modeling Language)
muy usado en el diseño de sistemas orientados a objetos.
Decimos que el gráfico creado por Bluej es simplificado, pues en UML el rectángulo que
representa una clase contiene una lista de sus atributos y una lista de sus métodos más relevantes
(además del nombre de la clase). Y el conjunto de tipos de relaciones manejadas por UML en general,
es más amplio que el manejado por BlueJ. Podemos decir que BlueJ aplica un subconjunto de
elementos de UML para facilitar la tarea del programador (se dice que BlueJ aplica técnicas de
Modelado Rápido).
Por ejemplo, tomando la clase Cuenta y la clase Principal que hemos usado como modelo
hasta aquí, podemos notar que la clase Principal (a través de declaraciones hechas en el método
main()) usa un objeto de la clase Cuenta. Esto marca lo que se conoce en UML como una relación de
uso o relación de dependencia: si los objetos de una clase A usan objetos de la clase B, hay una
relación de uso entre A y B y se grafica con una línea de rayas discontinua terminada en punta de
flecha abierta, originada en la clase A y terminada en la clase B:
Vimos en la Ficha anterior que en Java un objeto se crea usando el operador new, el cual
reserva memoria para el objeto creado, invoca a un constructor de la clase para iniciar sus atributos, y
finalmente retorna la dirección de memoria del objeto creado. Esa dirección se almacena en una
variable que recibe el nombre genérico de variable referencia:
En el fragmento anterior, la primera linea declara una variable a de tipo Cuenta. Esa variable
no es un objeto de la clase Cuenta, sino que puede contener la dirección de un objeto de la clase
Cuenta. Hasta que ese objeto se cree, podemos asumir en general que la variable a contiene lo que se
conoce como la dirección nula (null):
a null
La segunda linea es la que usando new crea un objeto de la clase Cuenta, y lo inicializa
invocando al constructor sin parámetros de la clase. Si suponemos que el objeto quedó guardado en
memoria en la dirección 52C4, entonces new retornaría ese valor y lo asignaría en la referencia a,
quedando así la memoria:
52C4
0 numero
0 saldo un objeto de la clase Cuenta
‘C’ tipo
dirección de memoria del
objeto creado por new
Como se ve, una cosa es la referencia y otra cosa es el objeto creado con new. Sin embargo,
una vez que la referencia contiene la dirección del objeto, se usa esa referencia para manejar al objeto
(se dice que la referencia es un manejador del objeto). En un gráfico es común que en lugar de mostrar
el número que representa la dirección, se muestre simplemente una flecha indicando que la referencia
apunta al objeto (y esto quiere decir lo que ya dijimos: la referencia contiene la dirección del objeto).
El gráfico siguiente equivale conceptualmente al anterior:
0 numero
0 saldo
‘C’ tipo
Cuando decimos que una referencia que apunta a un objeto se usa como un manejador para ese
objeto, queremos decir que a partir de allí la referencia se usa como si ella fuera el objeto, y a través
de ella se invocan los métodos públicos del objeto (mediante el operador punto colocado entre la
referencia y el nombre del método):
a.setNumero(200);
a.setSaldo(3000);
a.setTipo(‘C’);
Notar que si una variable referencia vale null, la misma no puede usarse para acceder a los
miembros del objeto, simplemente porque el objeto no existe. El intento de hacerlo provocará una
excepción de puntero nulo (NullPointerException) al ejecutar el programa, y el programa se
interrumpirá:
Cuenta a = null;
a.setNumero( 2034 ); // lanza una NullPointerException y el programa se interrumpe
A partir de aquí, el programador debe tener cuidado de entender bien lo que hace cuando opera
con referencias: supongamos que dos referencias a y b apuntan a dos objetos diferentes, creados
ambos con new. El gráfico de memoria se vería así:
b
a
0 numero 10 numero
0 saldo 2000 saldo
‘C’ tipo ‘I’ tipo
a = b;
entonces ambas referencias pasarán a apuntar al mismo objeto (el que originalmente era apuntado por
b). Por lo tanto, si se invoca un método desde la referencia a, tal como a.setNumero(20) para cambiar
el contenido de algún atributo de a, se estará modificando también el objeto referido por b. La
asignación a=b; NO COPIA los contenidos del objeto apuntado por b hacia el objeto apuntado por a.
Lo que copia es la dirección de b en la variable a:
b
a
0 numero 10 numero
objeto des-referenciado 0 saldo 2000 saldo
‘C’ tipo ‘I’ tipo
Técnicamente, el garbage collector liberará la memoria ocupada por esos objetos cuando
determine que se está próximo a una situación de out of memory (una situación en la que la memoria
está a punto de agotarse). De otro modo, aún cuando existan objetos perdidos, el garbage collector
posiblemente no llegue a activarse. El programador puede confiar en que el garbage collector no
afectará el rendimiento de su aplicación, pero también debe saber que no podrá intervenir en el
esquema de trabajo del mismo. A los sumo, el programador puede requerir que el garbage collector
considere intervenir y revisar la memoria en un momento dado, invocando al método System.gc().
Pero incluso en este caso, si el garbage collector determina que no hay peligro de out of memory, no
liberará la memoria ocupada por objetos perdidos:
Cuenta a, b, c;
i.) Se conocen dos números distintos. Calcular la superficie de un cuadrado, suponiendo como
lado del mismo al mayor de los números dados y la superficie de un círculo suponiendo como radio del
mismo al menor de los números dados.
Proponemos el siguiente modelo simple, desarrollado con BlueJ. Contaremos con dos clases
Cuadrado y Circulo, cada una de las cuales calculará el área de esas figuras. La primera tendrá un
atributo lado y la segunda un atributo radio. La clase Principal contendrá el main(), y se encargará de
la responsabilidad de determinar cuál de los dos números cargados es el mayor y cual es el menor.
Observe que el método toString() de cada una de las clases Cuadrado y Circulo invoca al método
superficie() de cada clase, y retorna una cadena que contiene ese valor (por ese motivo, main() no
tiene necesidad de pedirle al objeto cuadrado y al objeto circulo que calculen la superficie: al mostrar
los datos de ambos, aparecerán las superficies)
El esquema propuesto podría basarse en otras clases adicionales: por ejemplo, se podría contar
con una clase Ordenador que se encargue de representar dos números y ordenarlos de menor a mayor
(el método ordenar() podría estar en esta clase en lugar de estar en la clase Principal). Por el momento
nuestra solución quedará como se propuso incialmente:
Y el programa completo quedaría así (se listan una por una todas las clases, salvo la clase
Consola):
// *******************Clase Cuadrado*************************
public class Cuadrado
{
private float lado;
public Cuadrado()
{
lado = 0;
}
// *******************Clase Circulo*************************
public class Circulo
{
private float radio;
public Circulo()
{
radio = 0;
}
public Circulo(float r)
{
radio = r;
}
// *******************Clase Principal*************************
public class Principal
{
private static float a, b, men, may;
ordenar();
Cuadrado cuad = new Cuadrado(may);
Circulo circ = new Circulo (men);
System.out.println(cuad.toString());
System.out.print(circ.toString());
}
Ahora bien: ¿cuál es la ventaja de semejante cambio? En este modelo de solución hemos
tenido que programar considerablemente más líneas de código que en las soluciones originales de
Ing. Valerio Frittelli - 8
Ficha de Estudio: Gestión de Referencias y otros conceptos de Java
programación estructurada que dimos en la ficha en que apareció el problema por primera vez... y eso
sin considerar otras posibles clases que podrían haberse usado (como se sugirió con la clase
Ordenador...) El programa hace básicamente lo mismo que antes, pero ahora aparecen (sólo...) cuatro
clases en lugar de dos, y muchísimos métodos que a priori el simple enunciado del problema no
pedía...
Como ejemplo del poder del reuso de código, supongamos ahora que se nos pide desarrollar
un programa nuevo. El pedido podría ser el siguiente (basado en un problema que se dejó a modo de
ejercicio en una ficha anterior):
ii.) Se desea cargar por teclado un conjunto de números que representan los valores de los lados
de un conjunto de terrenos a la venta en un loteo. Cada terreno es un cuadrado, por lo que basta con saber
el valor de un lado para definir su superficie. El programa debe mostrar la superficie de cada terreno en el
cual se cumpla que el valor de cada lado es mayor a 20 metros.
Podemos ver que en este enunciado vuelve a aparecer la necesidad de trabajar con objetos de la
clase Cuadrado, y posiblemente con objetos de otras clases (¿una clase Loteo o Inmobiliaria que se
encargue del proceso de chequear lo pedido en el enunciado?)
Y bien: aparecen nuevas clases, pero al menos una ya está diseñada, programada y lista para
entrar a operar nuevamente: nuestra clase Cuadrado encaja perfectamente con este requerimiento. Y
ahora vemos que no fue una mala idea dotar a esa clase de tantos métodos de soporte como fuera
necesario: en el primer problema no se requirió de los servicios del método getLado(), pero se incluyó
de todos modos. Y ahora ese método será valioso para chequear si el lado es mayor a 20 o no. El
problema podría haber hablado del perímetro del cuadrado: eso no estaba previsto en nuestra clase
original, pero con poco trabajo podría incluirse ahora...
Podemos ver que si nos esforzamos en identificar clases pequeñas pero funcionalmente
completas, que sepan hacer bien un trabajo específico, tendremos muchas probabilidades de volver a
usar esa clase en otros proyectos, más adelante. O podría ocurrir que otros programadores de nuestro
equipo noten que nuestras clases les serían útiles en sus propios desarrollos... Al fin y al cabo, piense
lo que hizo por Usted la clase JOptionPane... esa clase propia de Java tiene programados muchos
métodos que no siempre se usan todos en un mismo programa, pero la clase está preparada para
responder si un programador la necesita... Y más modestamente, lo mismo vale para nuestra vieja
clase Consola...
Listamos sólo la clase Principal (que ahora tiene muy poco trabajo...) y la clase Loteo. La
clase Cuadrado ya se listó en el ejemplo anterior, y la clase Consola se usa como clase de soporte:
// ***************Clase Loteo********************
public class Loteo
{
private String nombre;
private Cuadrado terreno;
public Loteo()
{
nombre = "Remate Judicial";
}
//*************Clase Principal****************
public class Principal
{
public static void main (String args[])
{
Loteo loteo = new Loteo("El Cuadrado");
System.out.println("Loteo: " + loteo.getNombre());
loteo.procesar();
}
}
Ejercicios: Se plantean ahora una serie de ejercicios que servirán para que los alumnos pongan en
práctica los temas vistos hasta aquí. La idea es que para todos los ejercicios que siguen plantee un
proyecto Java e incluya clases en la forma más completa que le sea posible. Luego plantee un método
main() en una clase Principal para resolver el problema usando objetos de las clases que haya creado.
Trate de no incluir en esa clase Principal los métodos que específicamente resuelven el problema:
esfuércese para tratar de diseñar clases separadas que cuenten con esos métodos, y luego invóquelos
desde un método main(). También esté atento a la posibilidad de reusar clases que ya haya diseñado y
creado para otros programas, o que hayan sido creadas por sus profesores en algún ejemplo.
Como siempre, recomendamos encarecidamente que cada estudiante haga el esfuerzo de desarrollar
todos los ejercicios pedidos, o al menos que haga el intento. En clase, consulte las dudas que pudieran
haberle surgido. No se quede sin preguntar. Use también el foro del aula virtual si necesita trasladar
una duda a sus compañeros o a sus docentes mientras intenta plantear los ejercicios. Durante el
transcurso de las semanas siguientes, se publicarán en el aula virtual las soluciones sugeridas por los
docentes a cada uno de estos ejercicios. No lo olvide... nunca lo olvide: a programar, se
aprende programando.
a.) Se ingresa por teclado un conjunto de números, uno a uno. Contar cuantas veces se
presenta el valor 10, el 20, el 30 y el 40; y cuantos números distintos a esos se presentan. Cortar el
proceso cuando el número ingresado sea igual a -1.
b.) Se lee un conjunto de n valores enteros. Se desea saber si la suma de todos ellos, es
igual, mayor o menor que cero.
c.) Escribir un programa que lea n números enteros y calcule la suma de los valores
ingresados que sean mayores a 10.
d.) Escribir un programa que lea n números enteros y calcule y muestre su suma y su
promedio. Indique también si ese promedio es mayor o menor que 100.
e.) Desarrolle un programa que permita cargar por teclado las edades de un conjunto
personas, una a una por doble lectura. Considere que la carga de datos termina cuando aparezca una
edad negativa. Determine cuántas de esas edades eran menores a 18, cuántas eran mayores o iguales a
18 pero menores a 21, y cuántas eran mayores o iguales a 21. Determine también la edad promedio del
grupo.
f.) Se tiene un conjunto de rectángulos y por cada uno se carga por teclado su base y
su altura. Se pide mostrar el valor del área, la base y la altura, sólo de aquellos rectángulos cuyo
perímetro sea menor que un número p dado (o sea, ingresado como dato). Continuar hasta que la base
ingresada sea igual a cero.
a.) Desarrolle un programa que permita cargar por teclado un número a y otro número
b. El programa debe validar que a sea menor que b, y luego mostrar todos los múltiplos de 3 que estén
en el intervalo [a, b].
c.) Generar y mostrar los primeros n múltiplos de tres que no sean múltiplos de cuatro,
siendo n un dato que se carga por teclado.
d.) Cargue por teclado un número entero n, validando que n no sea negativo ni cero. El
programa debe determinar si el número es primo o no.
e.) Una fábrica de piezas metálicas circulares para automóviles necesita un programa
para procesar ciertos datos acerca de su producción en un período dado. Se requiere cargar por teclado
los datos del radio de cada tipo de pieza producida (cortar cuando el radio sea cero o negativo), y
realizar lo siguiente:
1.) Determinar cuántas piezas tienen una longitud de circunsferencia
(perímetro... o sea: 2*pi*radio) mayor a cierto valor x cargado por
teclado.
2.) Determinar la superficie promedio de todas las piezas cargadas.
f.) Escribir un programa que pida ingresar coordenadas (x, y) de un conjunto de puntos
(para x e y distintos de 0(cero)) e informe la cantidad de puntos que hay en cada cuadrante. El proceso
debe terminar al ingresar el par (0, 0).
g.) Un punto en un plano puede representarse como un objeto con dos atributos que
almacenen sus coordenadas (de hecho, posiblemente eso fue lo que Usted hizo en el ejercicio anterior...)
Desarrolle un programa con menú de opciones, que permita ingresar dos puntos a y b, representados
como objetos, y que contenga opciones para:
h.) Un número complejo tiene la forma c = a + b*i , donde a y b son números reales, y
el factor i es la unidad imaginaria, que se define como la raíz cuadrada del número (-1) aunque
simbólicamente, un número complejo simplemente puede representarse como c = (a , b*i) o incluso
como el par ordenado c = (a, b) si se asume que el factor i va implícito. Se puede pensar entonces en
una clase Complejo que use dos atributos para representar un número complejo, guardando en uno de
esos atributos el número a (parte real), y en el otro el número b (parte imaginaria). Formalmente, el
factor i (unidad imaginaria) se asume implícitamente en dicha representación, como acompañando al
Ing. Valerio Frittelli - 13
Ficha de Estudio: Gestión de Referencias y otros conceptos de Java