You are on page 1of 22

UAA – Sistemas Electrónicos Programación en Java Eduardo Serna-Pérez

1.3 Fundamentos del Lenguaje Java


En el paradigma Orientado a Objetos se dice que una Entidad u Objeto ésta compuesto de al
menos dos componentes fundamentales, por un lado, los datos o atributos que caracterizan a esa
entidad y por el otro las funciones o métodos que le permiten interactuar y comunicarse con el
medio en el cual se desenvuelve. De manera que un objeto es una entidad concreta (instancia) de
alguna clase que contiene propiedades e interactúa con otros objetos.

Un programa orientado a objetos puede ser considerado como un conjunto de objetos que
interactúan entre si para obtener un resultado, al contrario de la programación estructurada que
solo cuenta con un conjunto de subrutinas aisladas de los datos.

La Abstracción de datos es un proceso que involucra la conceptualización de un problema


identificando sus partes más importantes y poniendo atención en las similitudes y diferencias entre
las entidades de un conjunto, para extraer las características esenciales que lo distingan y evitar
las características no relevantes. Y así, se establece una sola representación del concepto que
tenga esas características pertinentes (clase).

En programación se puede ver a la abstracción como la capacidad de crear nuevos tipos de


datos que son definidos por el usuario extrayendo las propiedades esenciales de un concepto, sin
preocuparse de los detalles exactos de la implementación. Algunos simplemente definen esto
como la capacidad de enfocarse en lo esencial.

Modificadores de acceso
En un diagrama de clase podemos observar a la izquierda de los atributos y métodos un símbolo +,
– y #. Estos símbolos nos indican el nivel de acceso, es decir que esos elementos de la clase
podrán o no ser vistos desde fuera de la clase.

Existen varios niveles de acceso para variables y métodos, cada uno de ellos será empleado
según las necesidades de diseño y programación:

• Los miembros public (públicos) serán vistos desde cualquier punto fuera de la clase, es decir
que puede ser usado o manipulado por cualquier otra clase (programa).

• Los miembros private (privados) estarán únicamente disponibles por los métodos
pertenecientes a la clase, es decir que únicamente sus métodos hermanos pueden accederlos.

• Existen además los modificadores de acceso protegido (protected) y por defecto (default).
UAA – Sistemas Electrónicos Programación en Java Eduardo Serna-Pérez

Declaración e implementación
Los miembros public son frecuentemente reconocidos como la interfase del objeto debido a que
son los únicos elementos que pueden comunicarse con otros objetos. Por otro lado, se recomienda
que los atributos sean privados, pues esto asegura el encapsulado, ya que no permitiremos que
tomen valores que puedan poner en riesgo el buen funcionamiento del conjunto (clase). Se
recomienda construir el número necesario de métodos públicos que nos permitan manipular a
estos atributos. En su momento hablaremos de los accessors (getters / setters), métodos que son
los responsables de manipular aquellos atributos privados.

Declaración de clase
Para el diseño de las clases emplearemos la palabra clave class y especificamos el nombre de la
clase, posteriormente declaramos los atributos y métodos dentro del bloque de la clase.
Adicionalmente por el momento emplearemos dos modificadores que nos indicaran el tipo de
acceso a los miembros de la clase, public y private.

[public] class NombreClase


{
/* definición de variables miembro */
[<public><private><protected>] <tipo> lista_de_atributos;

/* declaración e implementación de constructores */


NombreClase( [lista_de_argumentos] ){
<tipo> lista_de_variables;
lista_de_sentencias;
...
}
/* declaración e implementación de métodos */
[<public><private><protected>] <tipo> metodo([lista_de_argumentos]){
<tipo> lista_de_variables;
lista_de_sentencias;
...
}
}

La palabra public nos indica que la clase será vista desde cualquier punto, la ausencia del
identificador de acceso indica que la clase únicamente será vista desde un grupo selecto llamado
paquete. Los miembros deben ser definidos dentro del bloque { … } de la clase. Es importante
recordar que todas las variables y funciones en Java deben pertenecer a alguna clase, ya que no
existen variables y funciones globales.

Declaración de atributos
Los atributos miembros declarados dentro de la clase como ya hemos visto son variables de una
clase particular de objeto y representan las propiedades o características más básicas de ese
objeto en particular. Las variables miembro o variables de instancia pueden ser declaraciones de
cualquier tipo de dato existente en Java, incluyendo objetos de cualquier otra clase que este
disponible dentro o fuera del paquete, de tal forma que los atributos miembros de la clase tendrán
modificador de acceso (public, private o default) que le permitirán determinar el tipo de acceso al
que esta disponible.

El atributo miembro suele ser declarado en la parte inicial de la clase justo antes de la declaración
de métodos miembro, así pues, datos miembros estarán disponibles de manera local a todas las
funciones de la clase por lo que no es necesario hacer referencia explicita a ellos, aunque en caso
de que sea muy necesario (resolución de ambigüedad), se emplea el identificador this para
reforzar la referencia.
UAA – Sistemas Electrónicos Programación en Java Eduardo Serna-Pérez

class A {
private int a, b, c; // a, b y c son enteras y privadas
public String nombre, apellido; // variables publicas
double arreglo[], valor; // variables por defecto
...
}

Los atributos de la clase solo serán declarados, hasta este punto no es necesario realizar una
inicialización de los datos ya que esta tarea la realizara el constructor de la clase.

Declaración de métodos
La comunicación con un objeto se realiza a través del paso de mensajes. El paso de mensajes es
el término utilizado para referirnos a la invocación de métodos. Los métodos miembros de la clase
son funciones que están relacionadas con un objeto en particular.

En un archivo de programa java es posible definir varias clases, pero no puede haber más de una
clase publica. En caso de definir una clase pública el archivo debe tener exactamente el mismo
nombre de la clase pública, con la extensión .java, por lo que el siguiente código estará presente
en un archivo llamado Main.java.

public class Main // clase ejecutora de la aplicación


{
public static void main( String[] args ) {
Ejemplo e = new Ejemplo(); // crea un objeto de clase
e.asignar( 1, 3, 6 ); // invoca al método asignar
e.mostrar();
System.out.println( "Suma " + e.sumarDatos() );
}
}

class Ejemplo
{
private int dato1, dato2, dato3; // datos miembro

public void asignar( int a, int b, int c ) { // método que asigna valores
this.dato1 = a;
this.dato2 = b;
this.dato3 = c;
}

public void mostrar() { // método para ver los datos


String salida = "dato1 "+dato1+" dato2 "
+dato2+" dato3 "+dato3;
System.out.println(salida);
}

public int sumarDatos() { // método que suma los datos


return (dato1 + dato2 + dato3);
}
}

Como podemos observar todos aquellos atributos y métodos que forman parte de una
determinada clase; y que representan sus características y propiedades particulares, encapsulan
estos elementos y los dota de ciertos criterios de visibilidad y manipulación con respecto a otras
clases. Además, la clase Ejemplo es declarada en la parte superior y posteriormente es declarada
una clase publica Main, que es la clase principal que se encargara de la ejecución del programa,
pues implementa el método main que es el responsable de iniciar la ejecución completa del
programa (punto de arranque). Entonces en la clase Main se crea una instancia (objeto) de la
clase Ejemplo a través del operador new para un objeto llamado e de tipo Ejemplo.
UAA – Sistemas Electrónicos Programación en Java Eduardo Serna-Pérez

La clase “Main” es la responsable de iniciar el proceso de ejecución pues es una clase publica que
contiene al procedimiento main, de esta manera la clase Ejemplo, contiene todos los elementos de
programación necesarios para ejecutar diversas acciones, así como un conjunto de datos
(propiedades) sobre los cuales realiza ciertas operaciones.

También es posible ver este ejemplo desde otra perspectiva en la cual no existe una clase principal
que funja las acciones de una directora de orquesta, es decir la responsable de la ejecución del
programa, de manera que es posible dentro de la misma clase incrustar la función main que sea la
responsable de la dirección del programa. De manera que solo existirá una sola clase que es la
encargada de realizar todas las acciones. Es importante recordar que ambos programas realizan
las mismas acciones, salvo que se tienen sus elementos distribuidos de una manera distinta, pero
en esencia el resultado es el mismo.

public class Ejemplo


{
private int dato1, dato2, dato3; // datos miembro

public void asignar( int a, int b, int c ) { // método que asigna valores
this.dato1 = a;
this.dato2 = b;
this.dato3 = c;
}

public void mostrar() { // método para ver los datos


String salida = "dato1 "+dato1+" dato2 "
+dato2+" dato3 "+dato3;
System.out.println(salida);
}

public int sumarDatos() { // método que suma los datos


return (dato1 + dato2 + dato3);
}

public static void main( String[] args ) {


Ejemplo e = new Ejemplo(); // crea un objeto de clase
e.asignar( 1, 3, 6 ); // invoca al método asignar
e.mostrar();
System.out.println( "Suma " + e.sumarDatos() );
}
}

Note que la clase Ejemplo crea un objeto de la misma clase empleando el operador new, de
manera que manda a llamar al constructor por defecto de la clase, el constructor de la clase
Ejemplo no se encuentra presente de forma específica, ya que el compilador lo introduce. Si en
algún momento el constructor fuera re-implementado, será necesario incrustar el constructor por
defecto de manera específica, si es el caso de que sea necesario en la ejecución.

Acceso a miembros de una clase


El acceso a los datos miembro y a las funciones miembro se realiza a través del operador “.”, de
manera que cualquier función miembro de la clase que sea publica, podrá ser ejecutada desde
fuere de ella empleando el operador de acceso a miembro. Para el caso de una función miembro
que ejecute a una función hermana, es decir, una función que se encuentra a su mismo nivel de
programación, podrá ser ejecutada directamente como si se tratara de una función local.

Las variables o propiedades de la clase también pueden ser accedidas de igual manera con el
operador “.”, aunque esta clase de acceso se determina en base a su nivel de alcance, es decir si
UAA – Sistemas Electrónicos Programación en Java Eduardo Serna-Pérez

se trata de datos públicos o privados. Es recomendable tener niveles de acceso en los datos y
funciones ya que esto conlleva a un buen diseño de la clase que pretendamos implementar.

Ejemplo e = new Ejemplo(); // crea un objeto de clase


e.asignar( 1, 3, 6 ); // invoca al método asignar
e.mostrar();
System.out.println( "Suma " + e.sumarDatos() );

1.3.1 Constructores y métodos get/set


Los constructores son las funciones mimbro que llevan el mismo nombre que la clase, en realidad
son las funciones que se ejecutan primero cuando se declara un objeto de una determinada clase.
El constructor es responsable de proveer de toda la infraestructura necesaria para que un objeto
pueda trabajar, por ejemplo, inicializa las variables a un valor dado, crea e inicializa las estructuras
dinámicas en tiempo de ejecución.

import java.util.Scanner;

public class Main1


{
public static void main(String [] args) {
Empleado empl1; // declaración de objetos
empl1= new Empleado(33,"Ana", 1250.00); //inicializa objetos
Scanner s = new Scanner(System.in);
System.out.print("clave ");
int clave = s.nextInt();
System.out.print("nombre ");
String nombre = s.next();
System.out.print("salario ");
double salario = s.nextFloat();
Empleado empl2 = new Empleado(clave, nombre, salario);
System.out.println("Los datos de uno ");
empl1.mostrar();
System.out.println("Los datos de dos ");
empl2.mostrar();
s.close();
}
}

class Empleado
{
private int clave;
private String nombre;
private double salario;

public Empleado(int clave, String nombre, double salario) {


this.clave = clave;
this.nombre = nombre;
this.salario = salario;
}

public void mostrar() {


System.out.println("clave "+ clave + " nombre "
+ nombre + " salario "+ salario);
}
}
UAA – Sistemas Electrónicos Programación en Java Eduardo Serna-Pérez

Sobrecarga de constructores
Los constructores pueden o no llevar argumentos, y en ningún caso regresan valor alguno, su nivel
de acceso es siempre publico, pues es necesario que se ejecuten desde fuera de la clase cuando
se crea una instancia de una clase. Cuando un constructor no lleva argumentos se le suele llamar
constructor por “default” o por defecto.

En muchos casos un solo constructor no será suficiente para describir el comportamiento completo
de nuestro objeto por lo que será necesario sobrecargarlo, es decir emplear en varias definiciones
de función el mismo nombre de la función constructora, cuidando que difieran el numero y tipo de
argumentos en cada función.

A continuación, presentamos un ejemplo que describa el comportamiento de los números


complejos, para ello crearemos una clase a la que llamaremos complejo. La clase complejo cuenta
al menos con dos miembros privados, un número flotante que representa la parte real del número
complejo, y un número flotante que represente la parte imaginaria del número:

class Complejo {
private double real, imag; // variables miembro

public Complejo() { } // constructor por defecto

public Complejo(double real, double imag) { // con argumentos


this.real = real; // inicializa el valor de las variables
this.imag = imag;
}

public Complejo( Complejo x ) { // constructor de copia


this.real = x.real;
this.imag = x.imag;
}

public void imprimir() {


System.out.println(real + (imag > 0 ? "+":"-") + imag + "i\n");
return;
}
}

Se crean 3 constructores que se encargan de inicializar los valores de la clase complejo:

El constructor sin argumentos inicializa el valor de las variables a cero, de manera que estén
disponibles para su uso. El constructor con argumentos recibe un par de valores que son en
realidad los elementos básicos de la clase complejo, tome en cuanta que ambas variables tienen el
mismo nombre (las de argumento y las locales a la clase) por lo que es necesario diferenciar entre
ellas empleando el operador this. El operador this le indica que las variables a las que se refiere
son a las del objeto (propietario de la llamada).

Hay 3 razones por las que se debe de sobrecargar las funciones constructoras:
• Ganar flexibilidad
• Permitir arreglos dinámicos
• Construir copias de objetos.

Así pues, la declaración para objetos de la clase complejo se realizaría de la siguiente manera:

public class TestComplejo {


public static void main(String [] arg) {
Complejo x = new Complejo();
Complejo y = new Complejo(1.1,2.2);
Complejo r = new Complejo( x );
UAA – Sistemas Electrónicos Programación en Java Eduardo Serna-Pérez

x.imprimir();
y.imprimir();
r.imprimir();
}
}

1.3.2 Uso del operador new


Para el caso de una clase cualquiera se contaría con varias versiones de constructores, finalmente
el operador new realizara la misma acción, reservar un bloque de memoria dentro del montículo
de memoria (memory heap) de manera que se creara una referencia a la cual estarán asociados
las variables de instancia.

Podemos observar en la figura que en la memoria de pila (memory stack), se encuentran todas
aquellas variables que hayan sido declaradas dentro de un método (variables locales), incluyendo
a los tipos de datos compuestos como los arreglos y objetos, solo que aquellos considerados como
objetos (arreglos e instancias) almacenan una dirección de memoria (un puntero que resulta
transparente para el programador). De manera que es muy importante recordar que TODO objeto
es una referencia de memoria dinámica que reserva espacio suficiente para las variables de
instancia.

Declaración e inicialización de referencias


Ahora veamos otro ejemplo donde podemos aplicar la definición de un constructor, por ejemplo,
suponga que se requiere un programa donde almacenemos la información relacionada con algún
empleado, con datos básicos como nombre del empleado y numero de identificación:

class Empleado {
public int clave;
public String nombre;
public double salario;

public Empleado() {}

public Empleado(int clave, String nombre, double salario) {


UAA – Sistemas Electrónicos Programación en Java Eduardo Serna-Pérez

this.clave = clave;
this.nombre = nombre;
this.salario = salario;
}

public void mostrar() {


System.out.println("clave "+ clave + " nombre "
+ nombre + " salario "+ salario);
}
}

El primero constructor se encargara de inicializar los valores, mientras que el segundo se


encargara de construir elementos de éste tipo a partir de datos independientes. Ahora la forma de
interactuar con esta clase es declarando algunos objetos o referencias de la clase e inicializarlos
con el operador new, de manera que se llame al constructor de la clase quien se encargara de
asignar el espacio necesario en memoria para los datos.

import java.util.Scanner;

public class Main {


public static void main(String [] args) {
Empleado [] vector; // vector de objetos
System.out.print("cuantos registros ?");
Scanner s = new Scanner(System.in);
int n = s.nextInt();
vector = new Empleado [n];
for (int i = 0; i < vector.length; i++) {
vector[i] = new Empleado();
System.out.print("clave ");
vector[i].clave = s.nextInt();
System.out.print("nombre ");
vector[i].nombre = s.next();
System.out.print("salario ");
vector[i].salario = s.nextFloat();
}
for (int i = 0; i < vector.length; i++)
vector[i].mostrar();
s.close();
}
}

El arreglo que se genera es un arreglo de referencias de manera que al momento de construirlo


solo se tiene un arreglo que esta inicializado con valores de null en cada casilla, por ello es
necesario mandar llamar al constructor para cada casilla.

Sobrecarga de métodos
Cuando dos o mas métodos comparten el mismo nombre y en tanto difieran en el tipo y/o número
de argumentos, y preferiblemente cada una de ellas contenga un código diferente, se dice que
están sobrecargadas (overloaded). De manera que para obtener la sobrecarga de funciones
simplemente hay que declarar y definir todas las versiones requeridas de la función.

El ejemplo emplea la sobrecarga de funciones tanto estaticas como de instancia. Java se hará
cargo de interpretar el argumento y decidir que función se hará cargo de él.
UAA – Sistemas Electrónicos Programación en Java Eduardo Serna-Pérez

public class A
{
public static void main(String [] x){
System.out.println( Sobrecarga.maximo(1,2) ); // método de clase
int [] a = {1,3,2};
System.out.println( Sobrecarga.maximo(a) ); // método de clase
Sobrecarga obj = new Sobrecarga();
System.out.println( obj.abs(-1) ); // método de instancia
System.out.println( new Sobrecarga().abs(-23F) ); // método de instancia
}
}

class Sobrecarga {
public int abs(int x) {
return x > 0 ? x : x*-1;
}
public float abs(float x) {
return x > 0 ? x : x*-1;
}
public static int maximo(int x, int y){
return x > y ? x : y;
}
public static int maximo( int [] array){
int n = array[0];
for( int i = 1; i < array.length; i++)
n = n > array[i] ? n : array[i];
return n;
}
}

1.3.3 Control de acceso a miembros y clases


Ahora veremos la manera de diseñar un programa que involucre un esquema orientado a objetos.
Para ello plantearemos un problema simple, se pretende construir un programa que sea capas de
administrar los datos de un grupo de estudiantes, los datos y la relación existente entre ellos se
muestra a continuación en un diagrama:

Manejo_alumno

Alumno - grupo : Alumno [ ]


- id : int + capturar : void
- nombre : String + mostrar : void
- apellido : String + ordenar : void
- faltasTotales : int Manejo_materia
- promedio : float
- materias : Materia [ ] Materia - materias : Materia [ ]
- nombre : String + capturar : Materia [ ]
- creditos : int + mostrar : void
- calificacion : float
- faltas : int

Del diagrama podemos inferir varias cosas, requerimos al menos 4 clases, una que defina los
datos relacionados con Alumno, otra para definir los datos de una Materia, además podemos
observar que Alumno requiere un conjunto de materias, además podemos ver que es necesario
contar con un par de clases que se encarguen de recabar la información necesaria de cada
Alumno y Materia, y son necesarias un par de clases que se encarguen de la manipulación.
UAA – Sistemas Electrónicos Programación en Java Eduardo Serna-Pérez

Generalmente una clase esta compuesta por un conjunto de atributos los cuales deberán de ser
manipuladas de alguna forma, debido a que los atributos de la clase generalmente se declaran
como privadas y sin por tanto inaccesibles, es necesario que exista un mecanismo que nos permita
su fácil manipualición.

Accessors (getters/setters)
Antes de continuar con el desarrollo del problema de alumnos y materias es necesario definir el
concepto denominado “accessors”, estos se definen como los métodos que son capaces de
interactuar con los atributos privados de una clase, debido a que los atributos son valores
inaccesibles, entonces es necesario contar con mecanismos que interactúen con ellos y se
preserve la encapsulación.

A continuación, se define una clase A con un par de atributos dato1 de tipo int y dato2 de tipo
String, ambos atributos tendrán sus respectivos métodos get y set que se encargarán de
interactuar de manera publica:

class A {
private int dato1;
private String dato2;

public void setDato1(int dato1) {


this.dato1 = dato1;
}
public int getDato1() {
return dato1;
}
public void setDato2(String dato2) {
this.dato2 = dato2;
}
public String getDato2() {
return dato2;
}
}

Los métodos setAtributo() se encargan de recolectar los datos relativos al atributo en cuestión, así
como los métodos getAtributo() se encargan de recuperar los valores relativos a cierto atributo
generalmente privados o protegidos.

Una vez aclarado el concepto de los métodos de acceso, prosigamos con el planteamiento
relativo al problema de los Alumnos y las Materias.

Para ello se plantea recolectar la información necesaria para un alumno, así como para el conjunto
de materias que cursa, en este caso el alumno además de sus datos cuenta con un arreglo de
materias donde almacenará la información relativa a cada materia que cursa, para este caso se
consideran 4 materias aún que el modelo se presta para que sea un numero variable empleando
listas ligadas. De momento conservaremos esta idea con arreglos para poder comenzar a
modelarlo.

La lista de cada método getter y setter correspondiente a cada atributo de la clase Alumno, así
como para la clase Materia se muestra a continuación:

public class Alumno {


private int id, faltasTotales;
private String nombre, apellido;
private float promedio;
private Materia materias[];

public Alumno() {}
UAA – Sistemas Electrónicos Programación en Java Eduardo Serna-Pérez

public Alumno(int id, String nombre, String apellido,


int faltasTotales, float promedio, Materia[] materias) {
this.id = id;
this.nombre = nombre;
this.apellido = apellido;
this.faltasTotales = faltasTotales;
this.promedio = promedio;
this.materias = materias;
}

public int getId() {


return id;
}
public void setId(int id) {
this.id = id;
}

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;
}

public int getFaltasTotales() {


return faltasTotales;
}
public void setFaltasTotales(int faltasTotales) {
this.faltasTotales = faltasTotales;
}

public float getPromedio() {


return promedio;
}
public void setPromedio(float promedio) {
this.promedio = promedio;
}

public Materia[] getMaterias() {


return materias;
}
public void setMaterias(Materia[] materias) {
this.materias = materias;
}
}

Las materias están organizadas de la siguiente manera: se cuenta con el nombre, el número de
créditos, la calificación y el número de faltas, observe la declaración de cada getter y setter según
el atributo que le corresponda.

public class Materia {


private String nombre;
private int creditos, faltas;
UAA – Sistemas Electrónicos Programación en Java Eduardo Serna-Pérez

private float calificacion;

public Materia() {}

public Materia(String nombre, int creditos,


int faltas, float calificacion) {
this.nombre = nombre;
this.creditos = creditos;
this.faltas = faltas;
this.calificacion = calificacion;
}

public String getNombre() {


return nombre;
}
public void setNombre(String nombre) {
this.nombre = nombre;
}

public int getCreditos() {


return creditos;
}
public void setCreditos(int creditos) {
this.creditos = creditos;
}

public int getFaltas() {


return faltas;
}
public void setFaltas(int faltas) {
this.faltas = faltas;
}

public float getCalificacion() {


return calificacion;
}
public void setCalificacion(float calificacion) {
this.calificacion = calificacion;
}
}

Una vez desarrolladas las clases responsables de la recolección de los datos, procedemos a crear
las clases que se encargaran de recolectar la información relativa a esos datos, en primer lugar,
generaremos la clase Manejo_alumno responsable de interactuar con el usuario final pues
realizará los procesos de captura y despliegue de alumnos, para ello genera un arreglo que se
asocia a un elemento llamado grupo. Suponemos que un grupo es una colección homogénea de
individuos de allí el uso del arreglo. Se supone que al constructor le indicaremos el número de
alumnos asociados a un grupo.

import java.util.Scanner;

public class Manejo_alumno {


private Alumno [] grupo;

public Manejo_alumno(int tam) { // tam asigna el numero de alumnos


grupo = new Alumno [tam];
}

public void capturar() { // recaba la información de cada alumno


System.out.println("*** ALUMNO ***");
UAA – Sistemas Electrónicos Programación en Java Eduardo Serna-Pérez

for( int i = 0; i < grupo.length; i++)


grupo[i] = ingresar();
}
/* recolecta de manera individual cada alumno, es la responsable de
interactuar con el usuario final desde la consola */
private Alumno ingresar(){
Alumno al = new Alumno();
Scanner in = new Scanner(System.in);
System.out.print("ID ?");
do {
al.setId(in.nextInt());
} while(al.getId() < 0 || al.getId() > 10);
System.out.print("Nombre ?");
in.skip("\\s*\\n");
al.setNombre(in.nextLine());
System.out.print("apellido ?");
al.setApellido(in.nextLine());
/*
crea un objeto de tipo Manejo_materias, debido a que recabamos la
información relativa a cada materia, capturar en Materia regresa
un arreglo de materias que es fácilmente manipulado */
// Manejo_materia materia = new Manejo_materia();
// al.setMaterias( materia.capturar() );
al.setMaterias(new Manejo_materia().capturar());
al.setPromedio(promedioC(al.getMaterias()));
al.setFaltasTotales(promedioF(al.getMaterias()));
return al;
}

private float promedioC(Materia [] tem){ // calculamos el promedio


float prom = 0;
for (Materia i : tem)
prom += i.getCalificacion();
prom /= tem.length;
return prom;
}

private int promedioF(Materia [] tem){ // calculamos el total de faltas


int suma = 0;
for (Materia i : tem)
suma += i.getFaltas();
return suma;
}

public void mostrar(){


System.out.println("*** CONSULTA ***");
for(Alumno i : grupo)
imprimir(i);
}

private void imprimir(Alumno i){


System.out.println("ID: "+i.getId());
System.out.println("nombre: "+i.getNombre()+" "+i.getApellido());
System.out.println("promedio: "+i.getPromedio());
System.out.println("faltas: "+i.getFaltas());
Manejo_materia temp = new Manejo_materia(i.getMaterias());
temp.mostrar();
//new Manejo_materia(i.getMaterias()).mostrar();
}
/*
Ordenamos el arreglo en base a dos criterios, mayor promedio y
UAA – Sistemas Electrónicos Programación en Java Eduardo Serna-Pérez

menor numero de faltas */


public void ordenar(){
for (int i = 0; i < grupo.length-1; i++)
for (int j = i+1; j < grupo.length; j++){
if (grupo[i].getPromedio() < grupo[j].getPromedio()) {
Alumno temp;
temp = grupo[i];
grupo[i] = grupo[j];
grupo[j] = temp;
}
else if (grupo[i].getPromedio() == grupo[j].getPromedio() &&
grupo[i].getFaltasTotales() > grupo[j].getFaltasTotales()) {
Alumno temp;
temp = grupo[i];
grupo[i] = grupo[j];
grupo[j] = temp;
}

}
}
}

La reasignación de referencias tiene un efecto transparente al usuario, pues al recorrer el arreglo el


resultado es exactamente el mismo que corresponde a su índice. La clase Manejo_materia realiza
un proceso similar al de la clase Manejo_alumno, recolecta la información y manda la referencia
de regreso a la clase Manejo_alumno.

import java.util.Scanner;

public class Manejo_materia {


private Materia [] materias;
/* inicializa con dos materias el arreglo */
public Manejo_materia() {
materias = new Materia [2];
}
/* recibe un arreglo de materias a referenciar */
public Manejo_materia(Materia[] materias) {
this.materias = materias;
}
/* recupera la informacion de las materias */
public Materia [] capturar(){
System.out.println("*** MATERIAS ***");
for (int i = 0; i < materias.length; i++)
materias[i] = ingresar();
return materias;
}
/* recupera cada materia del arreglo */
private Materia ingresar(){
Scanner in = new Scanner(System.in);
Materia m = new Materia();
System.out.print("Nombre de la materia?");
m.setNombre(in.nextLine());
System.out.print("creditos ?");
m.setCreditos(in.nextInt());
System.out.print("calificacion ?");
m.setCalificacion(in.nextFloat());
System.out.print("faltas ?");
m.setFaltas(in.nextInt());
return m;
}
/* muestra el contenido de cada materia */
UAA – Sistemas Electrónicos Programación en Java Eduardo Serna-Pérez

public void mostrar(){


for (Materia i : materias)
verMateria(i);
}

private void verMateria( Materia temp){


System.out.println("Materia:"+temp.getNombre());
System.out.println("creditos:"+temp.getCreditos());
System.out.println("faltas:"+temp.getFaltas());
System.out.println("calificacion:"+temp.getCalificacion());
}
}

Finalmente echamos a volar todo el mecanismo creando un objeto de la clase Manejo_alumnos


indicándole al constructor el numero de alumnos que pretendemos manejar, una vez establecido
este valor mandamos a ejecutar al proceso de capturar que es el responsable de realizar el alta de
los alumnos y sus materias, tome en cuenta que al capturar la información de las materias se
generara un calculo automático para el promedio y el numero de faltas total.

import java.util.Scanner;
public class Sistema {
public static void main(String[] args) {
System.out.print("Cuantos alumnos serán ?");
Scanner in = new Scanner(System.in);
int tamano = in.nextInt();
Manejo_alumno objeto = new Manejo_alumno(tamano);
objeto.capturar();
objeto.mostrar();
objeto.ordenar();
System.out.println("*** ORDENADO ***");
objeto.mostrar();
}
}

La idea principal en éste ejemplo es mostrar la forma en como se realiza la programación orientada
a objetos para problemas cotidianos, aquí tratamos de usar al máximo el concepto de orientación a
objetos ya que se construyen objetos bien definidos de diversas clases que interactúan entre sí
transmitiendo mensajes de un objeto a otro.

Ejemplos
En laboratorio

1.3.4 Construcción y acceso de paquetes


Un paquete es una forma de organizar las diversas clases que conforman a nuestro proyecto en
directorios bien definidos de manera que podamos también aclarar la existencia del nivel de
acceso por default (sin nivel de acceso), que nos indica que un atributo o método será acceso de
manera local a los objetos de vecindario (directorio).

Nuestra aplicación está construida dentro del directorio llamado paquete:

Home/paquete
datos/
Main.java

/* Main.java
* importamos las clases del paquete datos para que estén presentes */
import datos.*;
UAA – Sistemas Electrónicos Programación en Java Eduardo Serna-Pérez

public class Main {


public static void main (String [] args) {
A obj = new A();
obj.porDefecto = 1; // error valor accesible únicamente por paquete
obj.publico = 2; // accedido desde cualquier lado
obj.privado = 3; // error valor accesible únicamente por paquete
B obj2 = new B();
obj2.metodo();
}
}

Dentro del directorio de datos se encuentran las clases A y B:

Home/paquete/datos

/* A.java
* la sentencia packaged indica que la clase A habitara en el vecindario
de datos (directorio)*/
package datos;

public class A {
int porDefecto; // será accesible por el paquete datos
public int publico;
private int privado;// manipulado por objetos de la clase A dentro del paquete
}

/* B.java
* la sentencia package indica que la clase B habitara en el vecindario
de datos (directorio)*/
package datos;

public class B
{
A obj;
public void metodo() {
A obj = new A();
obj.porDefecto = 4; // es accesible pues viven en el mismo paquete
obj.publico = 5;
obj.privado = 6; // accesible dentro del paquete por objetos de A
}
}

Podemos concluir entonces que los elementos declarados de tipo default (sin nivel de acceso)
únicamente serán visibles dentro del directorio o paquete donde fueron declarados.

1.3.5 Variables y métodos estáticos


Cada objeto de una clase tiene su propia copia de todos los datos miembros de la clase. En ciertos
casos es deseable que todos los objetos de una clase compartan una sola copia de una variable
en especial. Por esta razón se utilizan las variables de tipo static, que presentan información a
nivel de clase.

A continuación un ejemplo de su declaración y uso, suponga que se requiere una clase Nave, que
será la responsable de crear tantos objetos tipo Nave como sea necesario para un juego de video.
De tal manera que será necesario tener una variable que sea la responsable de llevar el conteo del
numero total de naves existentes, de tal manera que al momento de que sean destruidas se
decrementa el numero de naves a seran visualizadas.
UAA – Sistemas Electrónicos Programación en Java Eduardo Serna-Pérez

class Nave
{
private int x,y; // coordenadas
private String nombre; // nombre de la nave
private int serie;
private static int count = 0; // variable de clase

Nave(int x, int y) { // el constructor puede manipular datos estáticos


this.x = x;
this.y = y;
this.serie = count++;
nombre = "Nave" + this.serie;
}

public String getNombre(){


return nombre;
}
public static int getCount() { // método de clase
return count;
}
public String cuadrante() {
return "("+x+","+y+")";
}
protected void finalize(){ // método destructor heredado de Object
count--;
System.out.println("destruyendo");
}
public String toString(){ // método heredado de Object
return " Nombre "+ this.nombre + " Cuadrante "+ this.cuadrante();
}
}

public class NaveApp


{
public static void main(String args[])
{
Nave n1 = new Nave(2,3);
System.out.println( n1.toString() );
Nave n2 = new Nave(10,20);
System.out.println( n2 );
System.out.println( Nave.getCount() );
n1 = null; // eliminamos la referencia
System.out.println("despues");
System.gc(); // llamamos a Grabage Collector
System.runFinalization();
System.out.println( Nave.getCount() );
}
}

Aún cuando los datos miembro static parezcan variables globales, estos tienen alcance de clase.
Los miembros static pueden existir aún si no se tienen ningún objeto de la clase, el acceso a estos
miembros debe ser a través de la clase misma, NombreClase.datoEstatico. Para el caso de
nuestro ejemplo no es accesible ya que cuenta con un modificador privado que impide su acceso.

También es posible crear funciones static, que resultan muy utiles cuando se trata de usarlar para
una tarea especifica donde no se vean involucrados los miembros del objeto, a estos métodos se
les conoce como métodos de clase y se referencian de la misma forma que las variables static,
NombreClase.metodoEstatico.
UAA – Sistemas Electrónicos Programación en Java Eduardo Serna-Pérez

1.3.6 Clases anidadas


En la estructura de la clase los miembros de una clases pueden ser otras clases, clases anidadas
u objetos. La utilización de clase anidadas se justifica cuando:

• Deben utilizarse varias instancias a1, a2, ... etc. de la misma clase.

• Los objetos de la clase A no pueden tener existencia independiente más que como miembros
de la clase contenedora B.

La situación puede esquematizarse como sigue:

class B
{
public class A { // clase anidada (clase miembro)
...
}
A a1, a2, a3; // Ok. B contiene objetos tipo A
...
}

El identificador de una clase anidada está sujeto a las mismas reglas de acceso que los restantes
miembros. Si una clase anidada se declara en la sección private de la clase circundante, la clase
anidada será utilizable sólo por los miembros datos de la clase que la circunde.

En caso de que la clase miembro sea public podrá ser accedida atreves de la clase que la
contiene empleando el operador de acceso a miembros: clase.subclase.

A continuación, un ejemplo del uso de clases anidadas; en esencia se verá que es un efecto muy
similar a emplear el esquema de estructuras. Es recomendable el uso de este tipo de esquemas
anidados de forma correcta y justificada.

Diseñaremos una clase que emplea clases anidadas, declararemos un vector de tipo Datos que
manipulara los datos estadísticos de una ciudad y el número de robos en esa ciudad. Los objetos
Histogram podrán capturar datos relativos al nombre de una ciudad y el número de robos en ella,
posteriormente mostrará los datos en forma de tabular y el número de robos para cada ciudad será
expresado adicionalmente con un histograma.

Desarrollaremos los siguientes métodos:

• Histogram( int ): método constructor que recibe como parámetro el numero de casillas que
se habilitaran para los datos del vector dinámico. El constructor se encargara de realizar el
ciclo para definir el tamaño del vector y asignará valores iniciales a cada componente del
vector.

• capturar( ): método encargado de capturar los datos entidad y frecuencia de robos.

• mostrar( ): método encargado de mostrar los datos del vector y el histograma relativo a la
frecuencia de robos en cada entidad.

import java.util.Scanner;

public class TestHistogram


{
public static void main(String [] args) {
int n;
UAA – Sistemas Electrónicos Programación en Java Eduardo Serna-Pérez

System.out.print("cuantos son? ");


Scanner s = new Scanner(System.in);
n = s.nextInt();
Histograma obj = new Histograma(n);
obj.capturar();
obj.mostrar();
s.close();
}
}

class Histograma
{
private Datos [] vector; // vector de estructuras
private class Datos { // crea la estructuras con una clase
public String ciudad;
public int frecuencia;
public Datos() {}
}
Histograma( int x ) {
this.vector = new Datos[x];
for (int i = 0; i < this.vector.length; i++)
this.vector[i] = new Datos();
}

public void capturar() {


Scanner s = new Scanner(System.in);
for (int i = 0; i < this.vector.length; i++) {
System.out.print("ciudad ");
this.vector[i].ciudad = s.next();
System.out.print("frecuencia ");
this.vector[i].frecuencia = s.nextInt();
}
}

public void mostrar() {


for (int i = 0; i < this.vector.length; i++) {
System.out.print( vector[i].ciudad + " " );
for ( int j = 0; j< vector[i].frecuencia; j++ )
System.out.print("*");
System.out.println();
}
}
}

La siguiente clase Romano emplea un mecanismo anidado para definir un arreglo de clases tipo
Tupla, encargado de almacenar la información relativa a los valores que representan cada
elemento involucrado en la generación del numero romano.

public class TestRomano {


public static void main(String[] args) {
Romano x = new Romano(1001);
System.out.println(x.getRomano());
System.out.println(new Romano("MMXI").getArabigo());
}
}

class Romano {
private String romano;
private int arabigo;

class Tupla { // clase interna que almacena el numero y su texto


UAA – Sistemas Electrónicos Programación en Java Eduardo Serna-Pérez

public String letra;


public int numero;

public Tupla(String letra, int numero) {


this.letra = letra;
this.numero = numero;
}
public Tupla() {}
}

// la tabla corresponde a los valores de cada numero romano


private Tupla [] tabla = {new Tupla("M",1000),new Tupla("CM",900),
new Tupla("D",500),new Tupla("CD",400),new Tupla("C",100),
new Tupla("XC",90),new Tupla("L",50),new Tupla("XL",40),
new Tupla("X",10),new Tupla("IX",9),new Tupla("V",5),
new Tupla("IV",4),new Tupla("I",1)};

public Romano() {}
/* recorre el vector buscando restar el valor correspondiente a cada
número e ir concatenando el valor en romano según sea el caso */
public Romano(int numero) {
StringBuffer cadena = new StringBuffer();
for(int i = 0; i < tabla.length; i++)
while (tabla[i].numero <= numero){
cadena.append(tabla[i].letra);
numero -= tabla[i].numero;
}
this.romano = cadena.toString();
}

public Romano(String numero) {


for(int i = 0; i < numero.length()-1; i++ ) {
if(this.valor(numero.charAt(i))<this.valor(numero.charAt(i)))
this.arabigo -= this.valor( numero.charAt(i) );
else
this.arabigo += this.valor( numero.charAt(i) );
}
this.arabigo += this.valor( numero.charAt(i) );
}

private int valor(char c){


for (Tupla t: tabla)
if ( t.letra.equals(String.valueOf(c)) )
return t.numero;
return 0;
}

public String getRomano() {


return romano;
}

public int getArabigo() {


return arabigo;
}
}
Una lista enlazada circular posee una característica adicional y es que su estructura parecería que
no tiene fin ya que el primero y el último nodo están unidos bajo una misma referencia. Para que la
lista sea sin fin, el puntero siguiente del último elemento apuntará hacia el 1er elemento de la lista
en lugar de apuntar al valor NULL.
UAA – Sistemas Electrónicos Programación en Java Eduardo Serna-Pérez

Para recorrer una lista enlazada circular podemos empezar por cualquier nodo y seguir la lista en
cualquier dirección hasta que se regrese hasta el nodo original. Desde otro punto de vista, las listas
enlazadas circulares pueden ser vistas como listas sin comienzo ni fin. Este tipo de listas es el más
usado para dirigir buffers para “ingerir” datos, y para visitar todos los nodos de una lista a partir de
un nodo dado.

raíz
info sig info sig info sig
primero último

nodo nodo nodo


|
Debido a que las listas circulares no cuentan de manera natural con un nodo primero y último,
debe de establecerse una convención para el manejo del nodo primero y último. De manera que el
nodo raíz apunta siempre al último nodo de la lista lo que le permite acceder de manera natural al
último nodo y la referencia raíz.sig le permite acceder al primer nodo de la lista como se muestra
en la figura anterior. Esta característica de las listas circulares facilita la inserción de nodos al inicio
o al final, así es posible generar pilas y colas dinámicas más fácilmente.

public class ListaCircular { // Clase Lista simple circular


private Nodo raiz;
private static int tamano;
class Nodo { // Clase Nodo
int dato;
Nodo sig;
public Nodo(int dato) {
this.dato = dato;
this.sig = null;
}
}

public boolean esVacia() { // metodo para saber si la lista esta vacia


return (raiz == null);
}

public void insertar(int dato) { //Metodo para insertar al final de la lista.


Nodo nuevo = new Nodo(dato);
System.out.println("entro " + nuevo.dato);
if( esVacia() ) {
nuevo.sig = nuevo;
raiz = nuevo;
}
else {
nuevo.sig = raiz.sig;
raiz.sig = nuevo;
raiz = nuevo;
}
tamano++;
return;
}

public Nodo extraer() { //Metodo para extraer el primer elemento de la lista


if( esVacia() ) {
System.out.println("La lista vacia");
return null;
}
Nodo tempo = raiz.sig;
raiz.sig = tempo.sig;
UAA – Sistemas Electrónicos Programación en Java Eduardo Serna-Pérez

this.tamano--;
return tempo;
}

public int getTamano() { //Metodo que imprime el tamaño de la lista.


return tamano;
}

//Metodo que imprime la lista y los valores ingresados.


public void mostrar() {
if (raiz == null)
return;
Nodo tempo = raiz.sig;
do {
System.out.print(tempo.dato + " ");
tempo = tempo.sig;
} while( tempo != raiz );
System.out.println(tempo.dato);
}

public static void main(String [] args) {


ListaCircular x = new ListaCircular();
x.insertar(1);
x.insertar(2);
x.insertar(3);
x.mostrar();
Nodo t = x.extraer();
System.out.println("elemento extraido " + t.dato);
x.mostrar();
}
}

Bibliografía

1. Grover, Radhika S., Programming with java: A multimedia approch, Jones and Bartlett
Publisher, 2013, ISBN: 9781449638610.
2. Paul J. Deitel, Harvey Deitel, JAVA: ¿Cómo programar?, 9a edición, Pearson Educación, 2012,
ISBN: 9786073211505

You might also like