Você está na página 1de 92

1.

DEFINE EL CONCEPTO DE IMPLEMENTACIÓN E ILUSTRE CINCO EJEMPLOS DE


ELLO

Después de declarar una clase se deben implementar (escribir el código de) sus
funciones y procedimientos, así como su constructor y su destructor.

El código de las funciones y procedimientos de una clase se escribe igual que el de las
funciones y procedimientos habituales, teniendo únicamente en cuenta que el nombre de
la función o procedimiento que se debe indicar será:

Nombre_Clase.Nombre_Procedimiento_o_Funcion

Es decir, el nombre de la clase seguido de un punto y del nombre del procedimiento o


función.

Por su parte, el constructor y el destructor se implementan de la misma forma y como si


fueran procedimientos, pero utilizando las instrucciones constructor y destructor
respectivamente en vez de la instrucción procedimiento.

Todo esto se ve en los ejemplos que siguen.

Ejemplo: Creación de una clase TPregunta para encapsular las preguntas de la


prospección

El siguiente ejemplo es especialmente interesante, pues implementa al completo una


clase que permite trabajar con las preguntas de la prospección a partir de unos objetos
que las encapsulan y que hacen el código especialmente legible.

Se incluye un cuerpo principal que realiza tres preguntas correspondientes al nombre,


primer apellido y segundo apellido del interlocutor (se entiende que son de tipo texto) y
finalmente monta el nombre completo en otro campo de la prospección (también de tipo
texto), a partir de la concatenación de los tres primeros.

//=============
TYPE
//=============

TPregunta = clase

protegido
_CodPregunta: cadena;
_CodCampo: cadena;
funcion _LeeValor(): variante;
procedimiento _EscribeValor(_valor: variante);
publico
constructor Crear(p, c: cadena);
destructor Destruir();
procedimiento Pregunta();
propiedad valor: variante leer _LeeValor
escribir _EscribeValor;
fin;
//=============================
// IMPLEMENTACION DE TPregunta
//=============================

//------------------------------------------------------------------
constructor TPregunta.Crear(p, c: cadena);
//------------------------------------------------------------------
inicio
_CodPregunta := p;
_CodCampo := c;
fin;

//------------------------------------------------------------------
destructor TPregunta.Destruir();
//------------------------------------------------------------------
inicio
// Nada a hacer
fin;

//------------------------------------------------------------------
funcion TPregunta._LeeValor(): variante;
//------------------------------------------------------------------
inicio
resultado := ValorProspeccion(_CodCampo);
fin;

//------------------------------------------------------------------
procedimiento TPregunta._EscribeValor(_valor: variante);
//------------------------------------------------------------------
inicio
AsignarValorProspeccion(_CodCampo, _valor);
fin;

//------------------------------------------------------------------
procedimiento TPregunta.Pregunta();
//------------------------------------------------------------------
inicio
Preguntar(_CodPregunta);
fin;

//================================================================
=
procedimiento DestruyePreguntas( pregs: tabla de TPregunta);
//================================================================
=
//
// Destruye un array de TPregunta, una a una.
//
var
i: entero;
inicio

Para i:=low(pregs) hasta high(pregs) hacer


pregs[i].Destruir();
fin;

//====
VAR
//====
Nombre, Apellido1, Apellido2, NombreCompleto: TPregunta;
// Preguntas de la prospección

//=================
// CODIGO PRINCIPAL
//=================
INICIO

// Inicializamos las preguntas


Nombre := TPregunta.Crear( 'P1', 'C1');
Apellido1 := TPregunta.Crear( 'P2', 'C2');
Apellido2 := TPregunta.Crear( 'P3', 'C3');
NombreCompleto := TPregunta.Crear( '', 'C4');

// Se realizan las preguntas


Nombre.Pregunta();
Apellido1.Pregunta();
Apellido2.Pregunta();

// Montamos el nombre completo


NombreCompleto.Valor := cadena(Nombre.Valor) + ' ' +
cadena(Apellido1.Valor) + ' ' +
Apellido2.Valor;

Aviso('El nombre completo es: ' + NombreCompleto.Valor);

// Destruimos los objetos de las preguntas


DestruyePreguntas( [Nombre, Apellido1, Apellido2, NombreCompleto]);

FinProspeccion(Cierto, '1');

FIN.

Otros ejemplos

En este primer ejemplo adicional, se declara e implementa una clase cuya utilidad sería
poder leer los registros de un cierto fichero de productos de un determinado proyecto:

(...)

TIPO

TProducto = Clase

Privado
_RegId: entero;
_HaCambiado: booleano;
funcion _ValorCodigo(): cadena;
funcion _ValorDescripcion(): cadena;
funcion _ValorFamilia(): entero;
funcion _ValorPrecio(): real;
funcion _NoSeleccionar(): booleano;
funcion _ValorOID(): entero;
funcion _LeerHaCambiado(): booleano;
procedimiento _AsignarOID(_OID: entero);
Publico
constructor Crear();
destructor Destruir();

propiedad Codigo: cadena leer _ValorCodigo;


propiedad Descripcion: cadena leer _ValorDescripcion;
propiedad Familia: entero leer _ValorFamilia;
propiedad Precio: real leer _ValorPrecio;
propiedad NoSeleccionar: booleano leer _NoSeleccionar;
propiedad OID: entero leer _ValorOID escribir _AsignarOID;
propiedad HaCambiado: booleano leer _LeerHaCambiado;
fin;

//***************************************//
//** Implementación de clase TProducto **//
//***************************************//

constructor TProducto.Crear();
inicio
_RegId := 0;
fin;

destructor TProducto.Destruir();
inicio
Si _RegId <> 0 entonces
LiberarRegistro(_RegId);
fin;

funcion TProducto._ValorFamilia(): entero;


inicio
Si _RegId = 0 entonces
Resultado := 0
sino inicio
Resultado := ValorRegistro(_RegId, CodConsultaFamilias + 'C4');
fin;
fin;

funcion TProducto._ValorPrecio(): real;


inicio
Si _RegId = 0 entonces
Resultado := 0
sino
Resultado := ValorRegistro(_RegId, 'C3');
fin;
funcion TProducto._NoSeleccionar(): booleano;
inicio
Si _RegId = 0 entonces
Resultado := Falso
sino
Resultado := ValorRegistro(_RegId, 'C5');
fin;

funcion TProducto._ValorOID(): entero;


inicio
Si _RegId = 0 entonces
Resultado := 0
sino
Resultado := ValorRegistro(_RegId, 'OID');
fin;

procedimiento TProducto._AsignarOID(_OID: entero);


inicio
Si _RegId <> 0 entonces
LiberarRegistro(_RegId);

Si (NO LeerRegistro('PRODUCTOS', ['OID'], [_OID], _RegId)) entonces


inicio
_RegId := 0;
fin;

_HaCambiado := Cierto;
fin;

funcion TProducto._LeerHaCambiado(): booleano;


inicio
Resultado := _HaCambiado;
_HaCambiado := Falso;
fin;

(...)

En este segundo ejemplo, se declara e implementa una clase que permite trabajar con
parejas de enteros, que pueden opcionalmente mantenerse ordenadas:

TIPO
TParejaEnteros = clase

Privado
procedimiento _Intercambia();

Protegido
_a, _b: entero;
_asignada: booleano;
_ordenar: booleano;

procedimiento _AsignaOrdenar(po: booleano);


Publico
constructor Crear();
destructor Destruir();

procedimiento InterCambia();
procedimiento Asigna(xx, yy: entero);
procedimiento Acerar();
funcion EsIgual( p: TParejaEnteros): booleano;
propiedad a: entero leer _a;
propiedad b: entero leer _b;
propiedad Ordenada: booleano leer _Ordenar
escribir _AsignaOrdenar;

fin;

constructor TParejaEnteros.Crear();
inicio
_a := 0;
_b := 0;
_ordenar := falso;
fin;

destructor TParejaEnteros.Destruir();
inicio
// Nada a hacer
fin;

procedimiento TParejaEnteros._AsignaOrdenar(po: booleano);


inicio
_ordenar := po ;
Si po Y (_b < _a) entonces _InterCambia();
fin;

procedimiento TParejaEnteros._Intercambia();
var
c: entero;
inicio
c := _b;
_b := _a;
_a := c;
fin;

procedimiento TParejaEnteros.InterCambia();
inicio
si NO _ordenar entonces _InterCambia();
fin;

procedimiento TParejaEnteros.Asigna(xx, yy: entero);


inicio
si _ordenar y (yy < xx) entonces
inicio
_a := yy;
_b := xx;
fin
sino
inicio
_a := xx;
_b := yy;
fin;

fin;

procedimiento TParejaEnteros.Acerar();
inicio
_a := 0;
_b := 0;
fin;

funcion TParejaEnteros.EsIgual(p: TParejaEnteros): booleano;


inicio
Resultado := (_a = p.a) Y (_b = p.b);
fin;

INICIO
...
FIN.

Declarar Clases Java

Ahora que ya sabemos como crear, utilizar y destruir objetos, es hora de aprender cómo
escribir clases de las que crear esos objetos.

Una clase es un proyecto o prototipo que se puede utilizar para crear muchos objetos. La
implementación de una clase comprende dos componentes: la declaración y el cuerpo de
la clase.

DeclaraciónDeLaClase {

CuerpoDeLaClase

La Declaración de la Clase

Como mínimo, la declaración de una clase debe contener la palabra clave class y el
nombre de la clase que está definiendo. Así la declaración más sencilla de una clase se
parecería a esto.

class NombredeClase {

...

}
Por ejemplo, esta clase declara una nueva clase llamada NumeroImaginario.

class NumeroImaginario {

...

Los nombres de las clases deben ser un identificador legal de Java y, por convención,
deben empezar por una letra mayúscula. Muchas veces, todo lo que se necesitará será
una declaración mínima. Sin embargo, la declaración de una clase puede decir más
cosas sobre la clase. Más especificamente, dentro de la declaración de la clase se puede.

• declarar cual es la superclase de la clase.


• listar los interfaces implementados por la clase
• declarar si la clase es pública, abstracta o final

Declarar la Superclase de la Clase

En Java, todas las clases tienen una superclase. Si no se especifica una superclase para
una clase, se asume que es la clase Object (declarada en java.lang). Entonces la
superclase de NumeroImaginario es Object porque la declaración no explicitó ninguna
otra clase. Para obtener más información sobre la clase Object, puede ver La clase
Object.

Para especificar explícitamente la superclase de una clase, se debe poner la palabra


clave extends más el nombre de la superclase entre el nombre de la clase que se ha
creado y la llave abierta que abre el cuerpo de la clase, así.

class NombredeClase extends NombredeSuperClase {

...

Por ejemplo, supon que quieres que la superclase de NumeroImaginario sea la clase
Number en vez de la clase Object. Se podría escribir esto.

class NumeroImaginario extends Number {

...

Esto declara explícitamente que la clase Number es la superclase de NumeroImaginario.


(La clase Number es parte del paquete java.lang y es la base para los enteros, los
números en coma flotante y otros números).

Declarar que Number es la superclase de NumeroImaginario declara implícitamente que


NumeroImaginario es una subclase de Number. Una subclase hereda las variables y los
métodos de su superclase.
Crear una subclase puede ser tan sencillo como incluir la clausula extends en su
declaración de clase. Sin embargo, se tendrán que hacer otras provisiones en su código
cuando se crea una subclase, como sobreescribir métodos. Para obtener más
información sobre la creación de subclases, puede ver Subclases, Superclases, y
Herencia.

Listar los Interfaces Implementados por la Clase

Cuando se declara una clase, se puede especificar que interface, si lo hay, está
implementado por la clase. Pero, ¿Qué es un interface? Un interface declara un conjunto
de métodos y constantes sin especificar su implementación para ningún método. Cuando
una clase exige la implementación de un interface, debe proporcionar la implementación
para todos los métodos declarados en el interface.

Para declarar que una clase implementa uno o más interfaces, se debe utilizar la palabra
clave implements seguida por una lista de los interfaces implementados por la clase
delimitados por comas. Por ejemplo, imagina un interface llamado Aritmetico que define
los métodos llamados suma(), resta(), etc... La clase NumeroImaginario puede declarar
que implementa el interface Aritmetico de esta forma.

class NumeroImaginario extends Number implements Aritmetico {

...

se debe garantizar que propociona la implementación para los métodos suma(), resta() y
demás métodos declarados en el interface Aritmetico. Si en NumeroImaginario falta
alguna implementación de los métodos definidos en Aritmetico, el compilador mostrará un
mensaje de error y no compilará el programa.

nothing.java:5: class NumeroImaginario must be declared abstract. It does not define

java.lang.Number add(java.lang.Number, java.lang.Number) from interface Aritmetico.

class NumeroImaginario extends Number implements Aritmetico {

Por convención, la clausula implements sigue a la clausula extends si ésta existe.

Observa que las firmas de los métodos declarados en el interface Aritmetico deben
corresponder con las firmas de los métodos implementados en la clase
NumeroImaginario. Tienes más información sobre cómo crear y utilizar interfaces en
Crear y Utilizar Interfaces.

Clases Public, Abstract, y Final

Se puede utilizar uno de estos tres modificadores en una declaración de clase para
declarar que esa clase es pública, abstracta o final. Los modificadores van delante de la
palabra clave class y son opcionales.
El modificador public declara que la clase puede ser utilizada por objetos que estén fuera
del paquete actual. Por defecto, una clase sólo puede ser utilizada por otras clases del
mismo paquete en el que están declaradas.

public class NumeroImaginario extends Number implements Aritmetico {

...

Por convención, cuando se utiliza la palabra public en una declaración de clase debemos
asegurarnos de que es el primer item de la declaración.

El modificador abstract declara que la clase es una clase abstracta. Una clase abstracta
podría contener métodos abstractos (métodos sin implementación). Una clase abstracta
está diseñada para ser una superclase y no puede ejemplarizarse. Para una discusión
sobre las clases abstractas y cómo escribirlas puedes ver Escribir Clases y Métodos
Abstractos.

Utilizando el modificador final se puede declarar que una clase es final, que no puede
tener subclases. Existen (al menos) dos razones por las que se podría querer hacer esto:
razones de seguridad y razones de diseño. Para una mejor explicación sobre las clases
finales puedes ver Escribir Clases y Métodos Finales.

Observa que no tiene sentido para una clase ser abstracta y final. En otras palabras, una
clase que contenga métodos no implementados no puede ser final. Intentar declarar una
clase como final y abstracta resultará en un error en tiempo de compilación.

Sumario de la Daclaración de una Clase

En suma, una declaración de clase se parecería a esto.

[ modificadores ] class NombredeClase [ extends NombredeSuperclase ]

[ implements NombredeInterface ] {

...

Los puntos entre [ y ] son opcionales. Una declaración de clase define los siguientes
aspectos de una clase.

• modificadores declaran si la clase es abstracta, pública o final.


• NombredeClase selecciona el nombre de la clase que está declarando
• NombredeSuperClase es el nombre de la superclase de NombredeClase
• NombredeInterface es una lista delimitada por comas de los interfaces
implementados por NombredeClase

De todos estos items, sólo la palabra clave class y el nombre de la clase son necesarios.
Los otros son opcionales. Si no se realiza ninguna declaración explícita para los items
opcionales, el compilador Java asume ciertos valores por defecto (una subclase de Object
no final, no pública, no obstracta y que no implementa interfaces).

2. DEFINA Y EXPLIQUE AMPLIAMENTE QUE ES UN MODIFICADOR DE ACCESO Y


QUE TIPOS EXISTEN

Modificadores
septiembre

Los modificadores son palabras clave que le entregan al compilador información acerca
de la naturaleza del código, datos o clases. Existen dos grandes grupos de
modificadores, los mas comunes son los modificadores de acceso public, protected y
private dejando en un segundo gran grupo otros como como final, abstract, static, native,
transient, synchronized, volatile..
public - Modificador de acceso que hace que un método o variable sea accesible para
cualquier otra clase.
protected - Modificador de acceso que hace que un método o variable sea accesible solo
por las clases que se encuentran en el mismo paquete o por las clases hijas
de la clase que lo contiene.
private - Modificador de acceso que hace que un método o variable sea accesible solo
para la clase que lo contiene.
final - Hace que sea imposible de extender, sobrecargar un
método o modificar el valor de una variable.
abstract - Palabra clave utilizada para declarar que una clase no puede ser instanciada o
para declarar que un método debe ser implementado en una clase hija no
abstracta.
static - Hace un método o variable relativo a la
clase y no a la instancia.
native - Indica que un método esta escrito en un lenguaje dependiente de la plataforma.
transient - Previene que un campo sea serializado.

synchronized - Indica que un método solo puede ser accedido por un hilo a la vez.

volatile - Indica que una variable debe ser leída siempre directamente de la
memoria principal. De esta forma se evita que se realicen copias locales al
hilo que puedan diferir entre si.
class - Palabra clave utilizada para definir una clase.

int - Tipo de dato de 32 bits entero con signo que puede tomar valores de -231 a 231-1.
System - Esta clase, que no puede ser instanciada, contiene muchas campos y
métodos útiles entre los cuales se encuentran los flujos de entrada y
salida estándar, la salida de error, el acceso a propiedades definidas
externamente, medios para cargar ficheros y librerías y un método para
rápidamente copiar una parte de un arreglo.
cadena de caracteres - "Tengo "

cadena de caracteres - " patas."

void - Se utiliza en la declaración de un método para especificar que no


hay valor de retorno.
String - Esta clase representa cadenas de caracteres. Todos las cadenas de
caracteres literales en los programas Java, como "el conejo que salta",
son implementadas como instancias de esta clase.
cadena de caracteres - "Programa."

this - Variable que hace referencia a la instancia actual de un objeto.

Serializable - La serialización de una clase es habilitada implementando esta


interfase. Todos las subclases de una clase serializable son
serializables. La interfase no tiene métodos o campos y se utiliza solo
para identificar la semántica de ser serializable.
Externalizable - Solo la identidad de la clase de una instancia Externalizable es
escrita el el flujo de una serialización y es responsabilidad de la
clase guardar y restaurar el contenido de su instancia. Los
métodos writeExternal y readExternal de la interfase son
implementados por la clase para darle el control completo sobre el
formato y contenido del flujo de para el objeto y sus clases padres.
Estos métodos deben estar explícitamente coordinados con su tipo
padre para salvar su estado. La serialización de objetos utilizan las
interfases Serializable y Externalizable y los mecanismos de
persistencia pueden utilizarlos también. Con cada objeto
almacenado se intenta con la interfase Externalizable, si el objeto
la soporta se llama al método writeExternal, si no lo soporta e
implementa Serializable es almacenado utilizando
ObjectOutputStream.
ObjectOutputStream - Escribe tipos de datos primitivos y gráficos de objetos en un
OutputStream. Los objetos pueden ser leídos (reconstruidos)
utilizando un objeto ObjectInputStream. El almacenamiento
persistente de objetos puede lograrse utilizando un fichero para
el flujo. Si el flujo es un socket de red, los objetos pueden ser
reconstruidos en otro host o en otro proceso.
FileOutputStream - Un flujo de salida para escritura de datos en un File o en un
FileDescriptor. Que este o no el fichero disponible o si es creado
depende de capas mas bajas de la plataforma. Algunas plataformas,
en particular, permiten que el fichero sea abierto para escritura solo
por un FileOutputStream (u otro objeto de escritura de ficheros) a la
vez. El algunas situaciones los constructores en esta clase suelen
fallar si el fichero involucrado ya está abierto.
OutputStream - No puedo especificar a que paquete
pertenece este objeto por falta de información
para resolver la ambiguedad.
Los candidatos son:
java.io de CORE v1.3.1
org.omg.CORBA.portable de CORE v1.3.1
org.omg.CORBA_2_3.portable de CORE
v1.3.1
ObjectInputStream - Deserializa datos primitivos y objetos previamente escritos
utilizando un ObjectOutputStream. ObjectOutputStream y
ObjectInputStream pueden proporcionar a una aplicación
almacenado persistente cuando se utilizan con FileOutputStream y
FileInputStream respectivamente. ObjectInputStream es utilizado
para recuperar esos objetos previamente serializados. Otros usos
incluyen intercambio de objetos entre hosts utilizando sockets o
para ordenar u desordenar argumentos y parámetros en un sistema
de comunicaciones.
default - En una sentencia switch ejecuta el
bloque de código si no se cumple
ninguna de las condiciones.
Los modificadores de acceso
Los modificadores de acceso controlan quienes pueden utilizar cada una de las
características de la clase que estamos definiendo. Las características de una clase son:
la misma clase, sus variables miembro, sus métodos y sus constructores.
Con raras excepciones, las únicas variables que pueden ser controladas por los
modificadores de acceso son las variables a nivel de clase. Aquellas que están
declaradas y se utilizan dentro de un método pueden no tener modificadores de acceso.
Esto tiene sentido ya que una variable de método solo puede ser utilizada dentro del
propio método.
Los modificadores de acceso son: public, protected y private.
El único modificador de acceso permitido en una clase que no es interna es public. Una
característica puede tener solo un modificador de acceso. Si una característica no tiene
modificador se dice que el acceso por defecto, que desafortunadamente no tiene un
nombre estándar. El acceso por defecto es llamado de tres formas: por defecto, de
paquete o amigable. A partir de ahora para referirnos a una característica que no tiene
modificador lo haremos utilizar el término por defecto.
public
Es el acceso mas permisivo. Una clase, variable o método público puede ser utilizado por
cualquier programa Java sin restricción. Un applet esta declarado como público, de esta
forma puede ser instanciado por un navegador. Una aplicación declara su método main()
público para que pueda ser invocado por el intérprete.
private
El acceso mas restrictivo es private. Las clases del nivel mas alto no pueden ser
declaradas privadas ya que haría imposible el acceso a ningún método o variable de
esta. Un método o variable privada solo puede ser instanciado en la clase que lo declara.
Las variables y métodos privados solo pueden ser accedidos por una instancia de la
clase que las declara ya que los modificadores de acceso indican que clases, no que
instancias pueden acceder a sus características. Las subclases tampoco pueden acceder
a variables y métodos privados.
Acceso por defecto
Acceso por defecto es el nombre que le vamos a dar al método de acceso utilizado por
clases, variables y métodos que no tienen especificado un modificador de acceso.
Las clases, métodos y variables con acceso defecto solo pueden ser accedidos por
clases que se encuentran dentro del mismo paquete.
Aun cuando no se estén programando clases dentro de un paquete, el compilador de
Java considera el directorio donde se encuentran las clases como un paquete por
defecto, de esta forma todas las clases que se encuentren en este directorio pueden
acceder a las clases, métodos y variables que tienen acceso por defecto.
protected
Aunque pueda ser mal interpretado, las variables, métodos y clases declaradas como
protegidas son mas accesibles que aquellas que tienen un acceso por defecto. Tienen
acceso a las variables, métodos y clases protegidas todas aquellas clases que estén
dentro del mismo paquete o sean subclases de la clase.
Subclases y la privacidad de métodos
Java especifica que los métodos no pueden ser sobrescritos para ser mas privados, esto
quiere decir que cuando heredamos y realizamos una sobrecarga o una sobrescritura, el
acceso nunca puede ser mas restrictivo. Cualquier métodos que sea sobrescrito con un
acceso mas restrictivo producirá un error de compilación.
Un método puede ser sobreescrito por un método con un acceso diferente siguiendo
algunas reglas:
• Un método privado puede ser sobrescrito por un método privado, por defecto, protegido o
público.
• Un método por defecto puede ser sobreescrito por un método por defecto, protegido o
público.
• Un método protegido puede ser sobrescrito por un método protegido o público.
• Un método público solo puede ser sobreescrito por un método público.
Otros modificadores
Java no tiene en cuenta el orden de aparición de los modificadores. Declarar una clase
public final no es diferente de declararla final public.
No todos los modificadores pueden aplicarse a todo tipo de característica. Veremos un
detalle de esto.
final
El modificador final se aplica a clases, métodos y variables. El significado de final varia
de contexto a contexto, pero la idea en si es la misma, la característica no puede ser
cambiada. Esto quiere decir que de una clase final no se puede derivar otra clase, una
variable final no puede ser modificada una vez que se le ha asignado el valor y un
método final no puede ser sobreescrito.
Aunque una variable final no puede ser cambiada, su contenido puede ser modificado. Si
esta variable apunta a un objeto, es perfectamente válido modificar las propiedades del
objeto.
abstract
El modificador abstract puede ser aplicado a clases y métodos. Una clase que es
abstracta no puede ser instanciada (esto es, no se puede llamar a su constructor). Las
clases abstractas se pueden utilizar como mecanismo para trasladar la implementación
de los métodos a la subclase. De esta forma, para poder utilizar una clase abstracta se
tiene que derivar y se tienen que implementar los métodos abstractos definidos en ellas.
El compilador obligará a implementar todos los métodos abstractos de la clase o declarar
la clase derivada abstracta. Si una clase tiene uno o mas métodos abstractos el
compilador insiste en que se debe declarar abstracta. También debe ser abstracta la
clase si hereda uno o mas métodos abstractos para el cual no provee implementación o
si la clase declara una interfase pero no proporciona implementaciones para cada uno de
sus métodos.
static
Este modificador puede ser aplicado a variables, métodos e inclusive a cierto código que
rompe un poco las reglas del lenguaje y que no es parte de un método. Se puede pensar
que una característica estática esta asociada a la clase en lugar de a la instancia
individual de la clase.
Esto quiere decir que para una variable animales existe solo una variable animales para
todas las instancias de la clase. Se puede hacer referencia a una variable estática
mediante una instancia de la clase o mediante el nombre de la clase.
Los métodos al igual que los datos, pueden ser declarados estáticos. Los métodos
estáticos solo pueden utilizar características estáticas y llamar a métodos estáticos. Estos
pueden ser invocados incluso antes de que alguna instancia de la clase sea construida.
No es posible acceder a una variable de instancia o a un método de instancia dentro de
un método estático. Al igual que las variables estáticas, los métodos estáticos no están
asociados a una instancia individual de su clase.
Si un método estático necesita acceder a una variable no estática o llamar a un método
no estático, debe especificar que instancia de su clase es dueña de la variable o ejecutar
el método.
Un método estático no puede ser sobreescrito de tal forma de que deje de ser estático.
Resumiendo, un método estático solo puede acceder a datos estáticos de su clase y no
puede acceder a métodos no estáticos. Un método solo puede llamar los métodos
estáticos de su clase y no puede llamar a métodos no estáticos. Un método estático no
tiene la variable this y no puede ser sobreescrito con un método no estático.
Inicializadores estáticos
Es legal para una clase contener código estático fuera del método de un cuerpo. Una
clase puede tener un bloque de código de inicialización simplemente rodeado de llaves
con la palabra estatic.
public class Gallina {
static int patas=2;
static {
System.out.prinln("Tengo " + patas + " patas.");
}
public static void main(String args[]) {
System.out.println("Programa.");
}
}
Este código es ejecutado exactamente una sola vez cuando la clase es cargada en el
orden en que esta escrito sin importar cuantos bloques haya. Hay que prestar mucha
atención con este tipo de construcciones y debe ser utilizado con precaución ya que el
abuso puede resultar en código confuso y poco claro.
native
Este modificador solo puede referirse a métodos, indica que el cuerpo de un método se
encuentra fuera de la máquina virtual de Java, en una librería. El código nativo esta
escrito en otro lenguaje de programación, típicamente C o C++ y compilado para una
sola plataforma por lo que la independencia de plataforma de Java es violada cuando
utilizamos este tipo de métodos.
transient
El modificador transient es solo aplicado a variables. Una variable transient no es
almacenada como parte del estado persistente de su objeto.
Muchos objetos que son declarados con interfases Serializable o Externalizable pueden
tener su estado serializado y escribir a destinos fuera de la máquina virtual. Esto se logra
pasando el objeto a el método writeObject() de la clase ObjectOutputStream. Si el objeto
es pasado a un FileOutputStream, entonces el estado del objeto es escrito en un fichero.
Si el objeto es pasado a un socket OutputStream, entonces el estado del objeto será
escrito en una red. En ambos casos el objeto puede ser reconstruido leyendo de un
ObjectInputStream.
Cuando hay cierta información que no se desea enviar como parte del objeto, como
puede ser cierta información delicada que quizás por razones de seguridad no deben ser
enviadas a través de un canal inseguro (una contraseña), se declara transient y esta no
es escrita durante la serialización.
synchronized
Este modificador es utilizado para controlar el acceso a código crítico en programas de
hilado múltiple. En artículos siguientes hablaremos en mas detalle de este modificador ya
que es casi imprescindible su uso cuando utilizamos hilado múltiple y debemos acceder a
propiedades o recursos desde varios hilos a la vez.
volatile
Este modificador solo lo mencionaremos para completar la lista. Solo las variables
pueden ser declaradas volatile. Esto quiere decir que pueden ser modificadas de forma
asincrónica para que el compilador tome las precauciones necesarias. Este tipo de
variables tiene especial interés para los ambientes con varios procesadores ya que
cuando un hilo modifica una variable de un objeto, este puede tener una copia local de la
variable y modificar esta. El problema surge cuando otro hilo lee la variable y el valor
recibido es el de la variable local al hilo. Esta puede diferir de la variable local del otro hilo
e inclusive de la variable almacenada en una suerte de "memoria principal". Al especificar
la variable como volatile se le indica a el compilador que la variable debe ser leída y
escrita directamente de la "memoria principal" cada vez que se necesita evitando tener
copias locales distintas.
Modificadores y características
No todos los modificadores pueden ser aplicados a todas las características. Las clases
de nivel superior no pueden ser protegidas y los métodos no pueden ser transient. En
cambio la palabra clave static es tan general que se puede aplicar hasta en bloques de
código flotantes.
La siguiente tabla muestra las posibles combinaciones entre características y
modificadores:

Modificador Clase Variable Método Constructor Bloque flotante

Public yes yes yes yes No

protected no yes yes yes No

default yes yes yes yes Yes

private no yes yes yes No

Final yes yes yes no No


abstract yes no yes no No

Static no yes yes no Yes

Native no no yes no No

transient no yes no no No

volatile no yes no no No

synchronized no no yes no Yes


Programa Java

Un programa Java se compone de un conjunto de clases que contienen variables de


diversos tipos utilizadas para almacenar datos, y métodos que implementan código
capaz de manipular dichas variables y crear objetos o instancias de clase, que permitan
la interacción con otros métodos y variables de esas clases.

El punto de inicio de todo programa Java es el código asociado al método main


(principal en inglés), es como la puerta de entrada del programa, lo primero que se
ejecuta.

A la clase que contiene al método main se la llama clase principal.

Las clases y métodos van entre llaves {} y al final de una instrucción o declaración de
variable debe escribirse un punto y coma (;). Se utilizan tabuladores para sangrar las
líneas de código con el fin de facilitar su legibilidad, aunque si se omiten no pasa nada.

Ejemplo:

Este programa consta de una clase de nombre PrimerSaludo, no declara ninguna


variable y cuenta con dos métodos de nombres main y mostrarMensaje. En programas
más complejos, se tendrán varias clases, en cada clase habrá muchos métodos y
declaraciones de variables, dentro de los métodos se declararán también variables, etc.
Por el momento, con esta sencilla clase nos conformaremos. Se va a explicar ahora cada
una de sus líneas.

La primera línea es la definición de una clase, la segunda es el método main o punto de


entrada del programa (siempre se define de esta forma), la tercera es una instrucción o
sentencia que muestra por la consola del DOS el mensaje entrecomillado, la cuarta sirve
para crear un objeto de la clase llamado ps mediante el que se llama o invoca a otros
métodos de la clase, la quinta es la llamada mediante el objeto ps al método
mostrarMensaje(), la sexta muestra por consola el mensaje entrecomillado. Luego
aparece la definición del método mostrarMensaje() cuyo código hace que se muestre por
consola el mensaje entrecomillado. Después de compilar y ejecutar este programa va a
mostrarse por consola lo siguiente:

Por consola:

Hola Jesus

Primera clase del curso de Java

FIN DEL PROGRAMA

A continuación se van a explicar qué son los métodos:

Es una de las herramientas fundamentales de cualquier lenguaje orientado a objetos.


Contienen código que persigue una serie de objetivos. En el ejemplo anterior, la clase
contenía dos métodos y sus objetivos eran:

• Objetivos del método main: mostrar un mensaje por consola, crear un objeto
de la clase PrimerSaludo, invocar al método mostrarMensaje mediante el objeto
anterior y mostrar otro mensaje por consola. Aparte de estos objetivos, el
fundamental y más importante es servir como punto de inicio de la ejecución del
programa.

• Objetivos del método mostrarMensaje: mostrar un mensaje por consola.

El programador, aparte de crear sus propios métodos como en el código anterior, puede
utilizar los que forman parte de la API (Application Programming Interface) estándar de
Java. La API se estudiará en siguiente sección.

DECLARACIÓN GENÉRICA DE UN MÉTODO: tiene la siguiente estructura

<modificadores de acceso> <tipo de dato de retorno> <nombre del método>(tipo1


arg1,tipo2 arg2,...){

Cuerpo del método;

Explicación de cada uno de los componentes de un método:

• Nombre del método: el que desee el programador.

• Número y tipo de argumentos asociados al método (tipo1 arg1,tipo2 arg2,...):


constituye su firma. Los argumentos pueden ser tanto variables primitivas como
variables referenciadas. Más adelante se estudiarán estos dos tipos de variables. Por
el momento, basta saber que las variables primitivas se emplean para almacenar
números, caracteres o valores lógicos, mientras que las referenciadas están
asociadas a objetos de clases.

• Tipo de dato de retorno o tipo de dato que devuelve el método:

• Dato asociado a una variable primitiva.

• Dato asociado a una variable referenciada.

• void. Es lo que debe ponerse cuando el método no devuelve nada y, por ejemplo,
simplemente muestra por consola un mensaje. Es el caso del método "void
mostrarMensaje()" del código anterior.

• Modificadores de acceso: public, private, protected y sin modificador. Sirven


para fijar el grado de accesibilidad de un método. Además de los de acceso, se
tienen otros como static, synchronized, final, abstract, etc. que afectan de un
determinado modo al método. Todos se irán estudiando a lo largo del curso.

• Cuerpo del método: código asociado al método.

NOTA 1: los argumentos asociados a un método se consideran variables locales, es decir,


accesibles sólo desde el cuerpo del método.

NOTA 2: la palabra clave o reservada return se emplea para truncar la ejecución de un


método cuando el tipo de dato que devuelve es void o para obtener lo que devuelve en el
caso de que no sea void. Suele aparecer al final del método. En Java, las palabras clave
tienen un significado especial para el compilador y no pueden utilizarse como nombres de
clases, métodos o variables. Se irán viendo durante el curso. Hay unas 50. A título de
ejemplo, todos los nombres de variables primitivas como byte, short, int, long, etc. son
palabras clave. También los modificadores de acceso, la palabra class, etc.

Fundamentos de las clases (C# y Java)

En las secciones siguientes se comparan los modificadores de C# y Java.

Modificadores de acceso

Los modificadores de C# son bastante similares a los de Java, con varias diferencias
pequeñas. Cada miembro de una clase, o la propia clase, se puede declarar con un
modificador de acceso para definir el ámbito de acceso permitido. Las clases no
declaradas dentro de otras clases sólo pueden especificar los modificadores públicos o
internos. Las clases anidadas, como otros miembros de clase, pueden especificar
cualquiera de los cinco modificadores de acceso siguientes:

• public

Visible para todos.


• protected

Visible sólo desde clases derivadas.

• private

Visible sólo dentro de la clase dada.

• internal

Visible sólo dentro del mismo ensamblado.

• protected internal

Visible sólo para el ensamblado actual o los tipos derivados de la clase


contenedora.

Modificadores public, protected y private


Un modificador public permite que el miembro esté disponible en cualquier parte, tanto
dentro como fuera de la clase. Un modificador protected indica que el acceso está
limitado al interior de la clase contenedora o las clases derivadas de ésta. Un modificador
private implica que el acceso sólo es posible desde dentro del tipo contenedor. En C#, el
modificador de acceso predeterminado es privado, mientras que en Java el acceso se
establece como predeterminado en cualquier parte desde dentro del paquete contenedor.

Modificador internal
A un elemento internal sólo se puede tener acceso desde dentro del ensamblado actual.
Un ensamblado de .NET Framework es casi equivalente a un archivo JAR de Java;
representa las unidades de creación a partir de las cuales se pueden crear otros
programas.

Modificador protected internal


Un elemento protected internal está visible sólo para el ensamblado actual o los tipos
derivados de la clase contenedora.

Modificador sealed
Una clase con el modificador sealed en la declaración de clase es lo opuesto a una clase
abstracta: no se puede heredar. Puede marcar una clase como sealed para evitar que
otras clases reemplacen su funcionalidad. Naturalmente, una clase con el modificador
sealed no puede ser abstracta. Observe también que a una estructura se le aplica
implícitamente el modificador sealed; por consiguiente, no se puede heredar. El
modificador sealed es equivalente a marcar una clase con la palabra clave final en Java.

Modificador readonly
Para definir una constante en C#, utilice el modificador const o readonly en lugar de la
palabra clave final de Java. El factor distintivo entre los dos modificadores de C# es que
los elementos const se tratan en tiempo de compilación, mientras que valores de los
campos readonly se especifican en tiempo de ejecución. Esto significa que la asignación
a los campos readonly se puede producir tanto en el constructor de clase como en la
declaración. Por ejemplo, la clase siguiente declara una variable readonly denominada
IntegerVariable que se inicializa en el constructor de clase:

public class SampleClass


{
private readonly int intConstant;

public SampleClass () //constructor


{
// You are allowed to set the value of the readonly variable
// inside the constructor
intConstant = 5;
}

public int IntegerConstant


{
set
{
// You are not allowed to set the value of the readonly variable
// anywhere else but inside the constructor

// intConstant = value; // compile-time error


}
get
{
return intConstant;
}
}
}
class TestSampleClass
{
static void Main()
{
SampleClass obj= new SampleClass();

// You cannot perform this operation on a readonly field.


obj.IntegerConstant = 100;

System.Console.WriteLine("intConstant is {0}", obj.IntegerConstant); // 5


}

public (Referencia de C#)

La palabra clave public es un modificador de acceso para tipos y miembros de tipos. El


acceso de tipo public corresponde al nivel de acceso menos restrictivo. No existen
restricciones para obtener acceso a los miembros públicos, como en este ejemplo:

class SampleClass
{
public int x; // No access restrictions.
}
.

En el siguiente ejemplo, se declaran dos clases, Point y MainClass. El acceso a los


miembros públicos x e y de Point se realiza directamente desde MainClass.

// protected_public.cs
// Public access
using System;
class Point
{
public int x;
public int y;
}

class MainClass
{
static void Main()
{
Point p = new Point();
// Direct access to public members:
p.x = 10;
p.y = 15;
Console.WriteLine("x = {0}, y = {1}", p.x, p.y);
}
}
Resultado
x = 10, y = 15
Si se cambia el nivel de acceso de public a private o protected, se aparecerá el siguiente
mensaje de error:

'Point.y' is inaccessible due to its protection level.

protected (Referencia de C#)

La palabra clave protected es un modificador de acceso a miembros. Un miembro


protegido es accesible dentro de su clase y por clases derivadas. Encontrará una
comparación de protected con los otros modificadores de acceso en Niveles de
accesibilidad.

Un miembro protegido de una clase base es accesible en una clase derivada sólo si el
acceso se realiza a través del tipo de la clase derivada. Por ejemplo, considere el
siguiente segmento de código:

// protected_keyword.cs
using System;
class A
{
protected int x = 123;
}

class B : A
{
static void Main()
{
A a = new A();
B b = new B();

// Error CS1540, because x can only be accessed by


// classes derived from A.
// a.x = 10;
// OK, because this class derives from A.
b.x = 10;
}
}

La instrucción a.x =10 genera un error, ya que A no se deriva de B.

Los miembros de una estructura no se pueden proteger, ya que la estructura no se puede


heredar.

Ejemplo

En este ejemplo, la clase DerivedPoint se deriva de Point; por lo tanto, puede obtener
acceso a los miembros protegidos de la clase base directamente desde la clase derivada.

// protected_keyword_2.cs
using System;
class Point
{
protected int x;
protected int y;
}

class DerivedPoint: Point


{
static void Main()
{
DerivedPoint dp = new DerivedPoint();

// Direct access to protected members:


dp.x = 10;
dp.y = 15;
Console.WriteLine("x = {0}, y = {1}", dp.x, dp.y);
}
}
Resultados
x = 10, y = 15
Comentarios
Si se cambian los niveles de acceso de x e y a private, el compilador producirá los
siguientes mensajes de error:

'Point.y' is inaccessible due to its protection level.

'Point.x' is inaccessible due to its protection level.

private (Referencia de C#)

La palabra clave private es un modificador de acceso de miembros. El acceso de tipo


private corresponde al nivel de acceso más restrictivo. Los miembros privados sólo son
accesibles dentro del cuerpo de la clase o estructura en la que se declaran, como en el
siguiente ejemplo.

class Employee
{
private int i;
double d; // private access by default
}
Los tipos anidados del mismo cuerpo también pueden tener acceso a esos miembros
privados.

Hacer referencia a un miembro privado fuera de la clase o estructura en la que se declara


produce un error de compilación.

En este ejemplo, la clase Employee contiene dos miembros de datos privados, name y
salary. Como miembros privados, sólo pueden tener acceso a ellos los métodos miembro;
por tanto, hay que agregar los métodos públicos denominados GetName y Salary para
permitir el acceso controlado a los miembros privados. Se tiene acceso al miembro name
a través del método público y se tiene acceso al miembro salary a través de una
propiedad pública de sólo lectura.

// private_keyword.cs
using System;
class Employee
{
private string name = "FirstName, LastName";
private double salary = 100.0;

public string GetName()


{
return name;
}

public double Salary


{
get { return salary; }
}
}

class MainClass
{
static void Main()
{
Employee e = new Employee();

// The data members are inaccessible (private), so


// then can't be accessed like this:
// string n = e.name;
// double s = e.salary;

// 'name' is indirectly accessed via method:


string n = e.GetName();

// 'salary' is indirectly accessed via property


double s = e.Salary
}
}

3. DE LOS DIFERENTES TIPOS DE MODIFICADORES DE ACCESO ILUSTRE 5


EJEMPLOS DE CADA UNO

Modificadores de acceso

Los modificadores de acceso sirven para restringir el acceso a los campos o a los
métodos de una clase.

Los principales modificadores de acceso son:

• private
• protected
• public
El modificador private permite que el campo o método sólo pueda ser accedido dentro de
la clase donde fue declarado.

El modificador protected permite que el campo o método sólo pueda ser accedido dentro
de la clase donde fue declarado y en todas las clases derivadas de ella.

El modificador public permite que el campo o método pueda ser accedido desde cualquier
clase.

4. EXPLIQUE AMPLIAMENTE EN QUE CONSISTE EL CONCEPTO DE


ENCAPSULAMIENTO DE LA CLASE

Definición previa: un paquete es una colección de clases que se encuentran en el mismo


directorio.

Las clases permiten implementar tipos de datos abstractos. El problema que se presenta
es que desde cualquier clase se puede accesar los campos de un objeto perteneciente a
otra clase. Esto es una violación al principio de abstracción que dice que un tipo de datos
abstracto sólo puede ser manipulado a través de las operaciones que se definen para
éste.

Visibilidad de Campos y Métodos

En java al definir una clase se puede controlar la visibilidad que tendrán sus campos y
métodos al exterior de la clase. Este control se efectúa de la siguiente forma:

class A
{
private int privx;
protected int protb;
public int pubc;
int paqd;

private void MetPriv() { ... }


protected void MetProt() { ... }
public void MetPub(A a)
{
// Un campo siempre es visible
// en la clase en donde se define
... privx ... a.privx ...
... protb ... a.protb ...
... pubc ... a.pubc ...
// Un método siempre es visible
// en la clase en donde se define
MetPriv(); a.MetPriv();
}
void MetPaq() { ... }
}
Las palabras private, protected y public son atributos de un campo o un método y su
significado es el siguiente:
• private: El campo o método sólo es visible dentro de la clase donde se define.
• protected: El campo o método es visible en la clase en donde se define y en
cualquiera de sus subclases.
• public: El campo o método es visible en cualquier clase.
• Ninguna de las anteriores: El campo o método es visible en cualquiera de las
clases pertenecientes al paquete en donde se define.

Ejemplo:
class B
{
public void MetB()
{
A a= new A();
a.pubc= 1; // Ok
a.priva= 2; // error, privado
a.protb= 3; // error, B no es
// subclase de A

a.MetPub(); // Ok
a.MetPriv(); // error, privado
}
}

Visibilidad de Clases

Al declarar una clase se puede especificar que es pública usando el atributo public. De
este modo la clase podrá ser usada por cualquier otra clase. Si la clase no es pública
entonces la clase sólo puede ser usada dentro del paquete que la contiene.

public class Box extends GraphObj


{ ... }
Podemos hacer un resumen de los atributos que pueden tener campos, métodos o
clases:

• Campo: {private | public | protected} {final} {static}


• Método: {private | public | protected} {final | abstract} {static}
• Clase: {public} {final | abstract}

Sólo se puede especificar uno de los atributos puestos en la misma llave.

El nombre de archivo
Un archivo puede contener a lo más una clase pública, en cuyo caso el nombre del
archivo debe ser el mismo de la clase pública, más la extensión .java. El resto de las
clases del archivo sólo serán visibles dentro del paquete. Es usual que los archivos
contengan una sola clase, pero también a veces se agregan otras clases al archivo
cuando éstas son usadas sólo dentro de ese mismo archivo.
En programación modular, y más específicamente en programación orientada a objetos,
se denomina encapsulamiento al ocultamiento del estado, es decir, de los datos
miembro, de un objeto de manera que sólo se puede cambiar mediante las operaciones
definidas para ese objeto.

Cada objeto está aislado del exterior, es un módulo natural, y la aplicación entera se
reduce a un agregado o rompecabezas de objetos. El aislamiento protege a los datos
asociados a un objeto contra su modificación por quien no tenga derecho a acceder a
ellos, eliminando efectos secundarios e interacciones.

De esta forma el usuario de la clase puede obviar la implementación de los métodos y


propiedades para concentrarse sólo en cómo usarlos. Por otro lado se evita que el
usuario pueda cambiar su estado de maneras imprevistas e incontroladas.

Encapsulamiento

Como se puede observar de los diagramas, las variables del objeto se localizan en el
centro o núcleo del objeto. Los métodos rodean y esconden el núcleo del objeto de otros
objetos en el programa. Al empaquetamiento de las variables de un objeto con la
protección de sus métodos se le llama encapsulamiento. Típicamente, el encapsulamiento
es utilizado para esconder detalles de la puesta en práctica no importantes de otros
objetos. Entonces, los detalles de la puesta en práctica pueden cambiar en cualquier
tiempo sin afectar otras partes del programa.

El encapsulamiento de variables y métodos en un componente de software ordenado es,


todavía, una simple idea poderosa que provee dos principales beneficios a los
desarrolladores de software: El encapsulamiento consiste en unir en la Clase las
características y comportamientos, esto es, las variables y métodos. Es tener todo esto en
una sola entidad. En los lenguajes estructurados esto era imposible. Es evidente que el
encapsulamiento se logra gracias a la abstracción y el ocultamiento que veremos a
continuación. La utilidad del encapsulamiento va por la facilidad para manejar la
complejidad, ya que tendremos a las Clases como cajas negras donde sólo se conoce el
comportamiento pero no los detalles internos, y esto es conveniente porque lo que nos
interesará será conocer qué hace la Clase pero no será necesario saber cómo lo hace.

Formas de encapsular

1. Estándar (Predeterminado)
2. Abierto : Hace que el miembro de la clase pueda ser accedido desde el exterior
de la Clase y cualquier parte del programa.
3. Protegido : Solo es accesible desde la Clase y las clases que heredan (a
cualquier nivel).
4. Cerrado : Solo es accesible desde la Clases.
En el encapsulamiento hay analizadores que pueden ser semánticos y sintácticos

Ocultación de información en C++

Todos hemos oido hablar de la encapsulación de información en los lenguajes orientados


a objetos y en C++. Vamos a ver aquí en qué consiste y algunos "trucos" que podemos
hacer en el caso concreto de C++ y que no suelen venir en los libros de este lenguaje
(aunque sí en libros sobre patrones de diseño).

Los puntos que veremos son:

• Encapsulamiento de los atributos de una clase


• Importancia de la encapsulación al compilar en C++
• Encapsulamiento a través de interfaces

5. ILUSTRE 5 EJEMPLOS DIFERENTES DONDE UTILIZE EL ENCAPSULAMIENTO DE


LA CLASE Y CUALES SON LOS ELEMENTOS QUE SE ESTAN ENCAPSULANDO

• Encapsulamiento de los atributos de una clase


• Antes de nada, debe quedar claro que el encapsulamiento, igual que cualquier
buen hábito de programación (como no poner goto, comentar, etc) es útil para
código que más adelante se puede querer reutilizar o modificar, por otras
personas o por uno mismo. Si yo hago un programa de marcianos y nunca jamas
pienso volver a tocarlo, da igual que lo haga con gotos y sin comentar mientras me
entere yo mismo mientras lo estoy haciendo y funcione. Pagaré este "pecado" si
dentro de dos meses se me ocurre mejorarlo o quiero reaprovechar algo de su
código para otro programa.
• Comento esto porque el encapsulamiento, llevado a su extremo, como es el
caso del punto final de interfaces, hace la programación un poco más complicada
(hay que hacer más clases). Este esfuerzo sólo se ve recompensado si el código
es muy grande (evitando recompilados innecesarios) o se va a reutilizar en un
futuro (podremos extraer clases con menos dependencias de otras clases). Dicho
esto, vamos al tema.
• Cualquier curso de orientación a objetos nos dice que es mejor poner los
atributos de una clase protegidos o privados (nunca públicos) y acceder a ellos a
través de métodos públicos que pongamos en la clase. Veamos el motivo.
Supongamos, por ejemplo, que nos piden un programa que permita llevar una lista
de gente con sus fechas de nacimiento. Entre otras cosas, decidimos hacernos
nuestra clase Fecha con varios métodos maravillosos de la siguiente manera.

class Fecha
{
public:
int anho; // El anho con cuatro cifras, ej. 2004
int mes; // El mes, de 1 a 12
int dia; // El dia, de 1 a 31
void metodoMaravilloso1();
void metodoMaravilloso2();
};

• Ya hemos hecho la clase. Ahora hacemos el resto del código y en unos varios
miles de líneas de código usamos directamente cosas como esta.

Fecha unaFecha;
unaFecha.anho = 2004;
unaFecha.mes = 1;
unaFecha.dia = 25;

• Finalmente acabamos nuestro programa y todo funciona de maravilla. Unos


días después nos dicen que el programa va a guardar tropecientas mil personas y
que ocupan mucho los ficheros, que a ver si podemos hacer algo para remediarlo.
¡Vaya!, almacenamos una fecha con tres enteros. Si usamos el formato de la
mayoría de los ordenadores, en el que la fecha es el número de segundos
transcurridos desde el 1 de Enero de 1970 (lo que nos devuelve la función time()),
basta con un entero.
• Total, que manos a la obra, cambiamos nuestra clase para que tenga lo
siguiente:

class Fecha
{
public:
/* Comentado por ineficiente
int anho;
int mes;
int dia; */

long numeroSegundos;

void metodoMaravilloso1();
void metodoMaravilloso2();
};

• Ya está hecho lo fácil. Ahora sólo hay que ir por las tropecientas mil líneas de
código cambiando nuestras asignaciones y lecturas a los tres enteros anteriores
por el nuevo long.
• Hubiera sido mucho mejor si hubieramos hecho estos tres enteros protegidos y
unos métodos para acceder a ellos. Algo como esto

class Fecha
{
public:
void tomaFecha (int anho, int mes, int dia);
int dameAnho ();
int dameMes ();
int dameDia ();
void metodoMaravilloso1();
void metodoMaravilloso2();
protected:
int anho; // El anho con cuatro cifras, ej. 2004
int mes; // El mes, de 1 a 12
int dia; // El dia, de 1 a 31
};

• Si ahora tenemos que hacer el mismo cambio, basta con cambiar los atributos
protegidos. Los métodos tomaXXX() y dameXXX() se mantienen en cuanto a
parámetros y valor devuelto, pero se modifica su código interno para que
conviertan el año,mes y dia en un long de segundos y al revés. El resto del código
no hay que tocarlo en absoluto.
• Es incluso mejor hacer los atributos privados que protegidos. Haciéndolos
protegidos, las clases hijas (las que heredan de Fecha) pueden acceder
directamente a estos atributos. Cuando hagamos el cambio por un long, debemos
cambiar también el código de las clases hijas. Si los atributos son privados y
obligamos a las clases hijas a acceder a ellos a través de métodos, tampoco
tendremos que cambiar el código de estas clases hijas.
• El acceso a través de métodos es menos eficiente que hacerlo directamente,
así que aunque siguiendo el principio de ocultación es mejor hacer atributos
privados, por eficiencia en algunos casos quizás sea mejor hacerlos protegidos (o
incluso públicos) a riesgo de tener que cambiar más líneas de código en caso de
cambio.

CONSEJO:
Siempre que sea posible hacer los atributos
de una
clase privados.

• Importancia de la encapsulación en C++


• Con lo contado hasta ahora evitamos tener que cambiar código en caso de
cambiar parámetros.
• En el caso concreto de C++ hay un pequeño problema adicional. Es bastante
normal hacer que las clases se definan por medio de dos ficheros. En el caso de
la clase Fecha tendríamos un Fecha.h con la definición de la clase y un Fecha.cc
(o .cpp) con el código de los métodos de la clase. Cuando queremos usar la clase
Fecha, solemos hacer nuestro #include <Fecha.h>.
• Cualquier proceso de compilado eficiente (como la utilidad make de linux y
supongo que el Visual C++) es lo suficientemente listo como para recompilar sólo
aquellos ficheros que es necesario recompilar. Es decir, si ya tenemos nuestro
proyecto compilado y tocamos un fichero, el compilador sólo compilará ese fichero
y todos los que dependen de él. Esta características es muy importante en
proyectos grandes (con muchos ficheros y muchas líneas de código), para ahorrar
tiempo de compilado cada vez que hacemos una modificación (He trabajado en
proyectos que tardaban en compilar desde cero alrededor de 4 horas).
• ¿Cual es el problema?. El problema es que si decidimos, por ejemplo, cambiar
nuevamente el atributo privado de la clase Fecha por otra cosa, necesitamos tocar
el fichero Fecha.h. Esto hará que se recompilen todos los ficheros que hagan
#include <Fecha.h> y todos los ficheros que hagan #include de algun fichero que
a su vez haga #include de Fecha.h y así sucesivamente.
• La solución es evidente, colocar lo menos posible en el fichero Fecha.h, en
concreto los #define y variables globales que no sea necesario ver desde otras
clases.
• Por ejemplo, nuestra clase Fecha podía tener unos #define para indicar cual es
el número mínimo y máximo de mes. Es mejor colocar estos #define en Fecha.cc
en vez de en Fecha.h, salvo que alguien tenga que verlos.

CONSEJO:
Siempre que sea posible, poner los #define,
definición de tipos,
constantes globales, etc, dentro del fichero
.cc

• Encapsulamiento a través de interfaces


• Nos queda una cosa. ¿Por qué tenemos que recompilar muchas cosas si
cambiamos un atributo privado de la clase?. Lo ideal sería poder cambiar las
cosas internas de la clase sin que haya que recompilar nada más, a fin de
cuentas, el atributo es privado y nadie lo utiliza directamente.
• Es bastante habitual en programación orientada a objetos el uso de interfaces
para hacer que las clases no dependan entre sí. En el caso de C++ el uso de
interfaces es útil además para evitar recompilados innecesarios.
• Una interface no es más que una clase en la que se definen los métodos
públicos necesarios, pero no se implementan. Luego la clase concreta que
queramos hacer hereda de esa interface e implementa sus métodos.
• En nuestro caso, podemos hacer una clase InterfaceFecha, con los métodos
públicos virtuales puros (sin código). Luego la clase Fecha hereda de
InterfaceFecha e implementa esos métodos.
• En el fichero InterfaceFecha.h tendríamos

class InterfaceFecha
{
public:
virtual void tomaFecha (int anho, int mes, int dia) = 0;
virtual int dameAnho () = 0;
virtual int dameMes () = 0;
virtual int dameDia () = 0;
virtual void metodoMaravilloso1() = 0;
virtual void metodoMaravilloso2() = 0;
};


De momento, ni siquiera existiría un InterfaceFecha.cc
• La clase Fecha sigue igual, pero hereda de InterfaceFecha.

#include <InterfaceFecha.h>

class Fecha : public InterfaceFecha


{
public:
void tomaFecha (int anho, int mes, int dia);
int dameAnho ();
int dameMes ();
int dameDia ();
void metodoMaravilloso1();
void metodoMaravilloso2();
protected:
int anho; // El anho con cuatro cifras, ej. 2004
int mes; // El mes, de 1 a 12
int dia; // El dia, de 1 a 31
};

• Ahora, todo el que necesite una Fecha, tiene que tener un puntero a
InterfaceFecha en vez de a Fecha. Alguien instanciará Fecha y lo guardará en ese
puntero. Es decir, podríamos hacer algo como esto

#include <Fecha.h>
#include <InterfaceFecha.>
...
InterfaceFecha *unaFecha = NULL;
...
unaFecha = new Fecha();
unaFecha->tomaFecha (2004, 1, 27);
...
delete unaFecha;
unaFecha = NULL;

• Si nos fijamos un poco, todavía no hemos arreglado nada, salvo complicar el


asunto. El que haga este código necesita hacer ahora #include tanto de
InterfaceFecha.h como de Fecha.h. Si tocamos algo en Fecha.h, este código se
recompilará.
• Este código necesita #include <Fecha.h> para poder hacer el new de Fecha.
Hay que buscar la forma de evitar ese new. Suele ser también bastante habitual
hacer una clase (o utilizar la misma Interface si el lenguaje lo permite, como es el
caso de C++) para poner un método estático que haga el new y nos lo devuelva.
• En el caso de Java, al poner este método, ya no tendríamos una interface, sino
una clase. Hacer que Fecha herede de InterfaceFecha nos limita a no heredar de
otra cosa (Java no admite herencia múltiple). Si esto es admisible, podemos
hacerlo así. Si necesitamos que Fecha herede de otra clase, en vez de poner el
método estático en la interface, debemos hacer una tercera clase aparte
GeneradorFecha con este método estático.
• En nuestro ejemplo de C++, la clase InterfaceFecha quedaría.

class InterfaceFecha
{
public:

static InterfaceFecha *dameNuevaFecha();

virtual void tomaFecha (int anho, int mes, int dia) = 0;


virtual int dameAnho () = 0;
virtual int dameMes () = 0;
virtual int dameDia () = 0;
virtual void metodoMaravilloso1() = 0;
virtual void metodoMaravilloso2() = 0;
};

• Ahora sí necesitamos un InterfaceFecha.cc. Dentro de él tendriamos

#include <InterfaceFecha.h>
#include <Fecha.h>

InterfaceFecha *InterfaceFecha::dameNuevaFecha()
{
return new Fecha();
}

• El código que antes utilizaba el puntero a InterfaceFecha quedaría ahora

#include <InterfaceFecha.>
...
InterfaceFecha *unaFecha = NULL;
...
unaFecha = InterfaceFecha::dameNuevaFecha();
unaFecha->tomaFecha (2004, 1, 27);
...
delete unaFecha;
unaFecha = NULL;
• Como vemos, sólo es necesario el #include de InterfaceFecha.h y este no
incluye a Fecha.h (lo hace InterfaceFecha.cc, no el .h). Hemos hecho que este
código no vea en absoluto a Fecha.h. Ahora podemos tocar sin ningún miramiento
el fichero Fecha.h, que este código no necesita ser recompilado.
• Una ventaja adicional es que se puede cambiar la clase Fecha por otra clase
Fecha2 en tiempo de ejecución. Bastaría con poner un atributo estático en
InterfaceFecha para indicar qué clase Fecha queremos y hacer que el método
dameNuevaFecha() instancie y devuelve una u otra en función de ese atributo.

CONSEJO:
Utilizar interfaces para aquellas clases que
preveamos que
pueden cambiar durante el desarrollo del
proyecto o
que creamos que podemos cambiar más
adelante por otra.

Este mecanismo de obtener una instancia de una clase a través de un método estático y
de una interface, para no depender de la clase concreta, creo que dentro del mundo de
los patrones de diseño es el patrón Factoria.

6. EXPLIQUE AMPLIAMENTE COMO SE COMUNICAN LOS ELEMENTOS QUE


FORMAN PARTE DE UN PROGRAMA ORIENTADO A OBJETOS.

Un objeto es la unidad individual que en tiempo de ejecución realiza las tareas de


un programa. Estos objetos interactúan unos con otros, en contraposición a la visión
tradicional en la cual un programa es una colección
de subrutinas (funciones o procedimientos), o simplemente una lista
de instrucciones para el computador. Cada objeto es capaz de recibir mensajes,
procesar datos y enviar mensajes a otros objetos de manera similar a un servicio.
Los objetos son unidades independientes que se comunican entre ellos mediante
mensajes.
Un objeto es inútil si está aislado. El medio empleado para que un objeto interactúe con
otro son los mensajes. Hablando en términos un poco más técnicos, los mensajes son
invocaciones a los métodos de los objetos.
Estas interacciones suponen ciertos tipos de relaciones entre los objetos del sistema. La
semántica que expresa un objeto en el sistema está determinado en primer lugar, por las
relaciones que éste establece con otros objetos o conjunto de objetos. Tomemos como
ejemplo un objeto fecha, del que sin establecer ningún tipo de relación, podría decirse
que significa un día del año particular. Pero si relacionamos ese objeto fecha con un
objeto Persona de manera que represente la fecha en que esa persona nació, en ese
contexto dado, el mismo objeto fecha adoptaría un significado diferente, el de
un cumpleaños, aunque sigue siendo una fecha, ahora tiene otra idea asociada. Las
relaciones entre objetos no solo están definidas por los objetos que participan y la
circunstancia que los relaciona, sino también por la cantidad de objetos (cardinalidad de
la relación) y la dirección de la misma. Una relación puede tener cardinalidad:
 uno a uno, ejemplo: un auto tiene un motor
 uno a muchos, ejemplo: un auto tiene muchas ruedas
 muchos a muchos, ejemplo: un auto se puede servir en muchas gasolineras y una
gasolinera puede servir a muchos autos.

y direccionalidad:

 unidireccional, ejemplo: un auto tiene cuatro ruedas.


 bidireccional

Las relaciones entre objetos más generales son las siguientes:

Composición

La composición (también conocida como relación asociativa) es un tipo de relación que se


establece entre dos objetos que tienen comunicación persistente. Se utiliza para expresar
que un par de objetos tienen una relación de dependencia para llevar a cabo su función,
de modo que uno de los objetos involucrados está compuesto por el otro.
De manera práctica, es posible reconocer asociatividad entre dos objetos A y B si la
proposición "A tiene un B" (o viceversa) es verdadera. Por ejemplo: "una computador
tiene un disco duro" es verdadero, por tanto un objeto computador tiene una relación de
composición con al menos un objeto disco duro.
Uso
Un objeto usa (conoce) a otro cuando puede enviarle mensajes, por ejemplo, para
requerir de este algún servicio. La composición puede verse como un caso particular de
esta relación.
Delegación
En ocasiones para lograr flexibilidad de diseño, un objeto es implementado de forma tal
que este delegue parte de su funcionalidad en otro objeto. Esto es muy común en
aplicaciones que hacen uso de interfaces graficas de usuario, en las que los controles
gráficos generales delegan la acción que se ejecutará ante determinado estímulo en otro
objeto.

7- DEFINE Y EXPLIQUE EL CONCEPTO DE METODO Y EXPLICA AMPLIAMENTE


PARA QUE SIRVE Y COMO SE UTILIZA

Los métodos son funciones que pueden ser llamadas dentro de la clase o por otras
clases. La implementación de un método consta de dos partes, una declaración y un
cuerpo. La declaración en Java de un método se puede expresar esquemáticamente
como:

tipoRetorno nombreMetodo( [lista_de_argumentos] ) {


cuerpoMetodo
}
En C++, el método puede declararse dentro de la definición de la clase, aunque también
puede colocarse la definición completa del método fuera de la clase, convirtiéndose en
una función inline. En Java, la definición completa del método debe estar dentro de la
definición de la clase y no se permite la posibilidad de métodos inline, por lo tanto, Java
no proporciona al programador distinciones entre métodos normales y métodos inline.

Los métodos pueden tener numerosos atributos a la hora de declararlos, incluyendo el


control de acceso, si es estático o no estático, etc. La sintaxis utilizada para hacer que un
método sea estático y su interpretación, es semejante en Java y en C++. Sin embargo, la
sintaxis utilizada para establecer el control de acceso y su interpretación, es muy diferente
en Java y en C++.

La lista de argumentos es opcional, tanto en Java como en C++, y en los dos casos
puede limitarse a su mínima expresión consistente en dos paréntesis, sin parámetro
alguno en su interior. Opcionalmente, C++ permite utilizar la palabra void para indicar que
la lista de argumentos está vacía, en Java no se usa. Los parámetros, o argumentos, se
utilizan para pasar información al cuerpo del método.

La sintaxis de la declaración completa de un método es la que se muestra a continuación


con los items opcionales en itálica y los items requeridos en negrilla:

especificadorAcceso static abstract


final native synchronized tipoRetorno nombreMetodo( lista_de_argumentos )
throws listaEscepciones

especificadorAcceso, determina si otros objetos pueden acceder al método y cómo


pueden hacerlo. Está soportado en Java y en C++, pero la sintaxis e interpretación es
considerablemente diferente.

static, indica que los métodos pueden ser accedidos sin necesidad de instanciar un objeto
del tipo que determina la clase. C++ y Java son similares en el soporte de esta
característica.

abstract, indica que el método no está definido en la clase, sino que se encuentra
declarado ahí para ser definido en una subclase (sobreescrito). C++ también soporta esta
capacidad con una sintaxis diferente a Java, pero con similar interpretación.

final, evita que un método pueda ser sobreescrito.

native, son métodos escritos es otro lenguaje. Java soporta actualmente C y C++.

synchronized, se usa en el soporte de multithreading, que se verá también en este


Tutorial.

lista_de_argumentos, es la lista opcional de parámentros que se pueden pasar al método

throws listaExcepciones, indica las excepciones que puede generar y manipular el


método. También se verán en este Tutorial a fondo las excepciones en Java.
8.-TOMANDO COMO EJEMPLOS DE PROGRAMACION CINCO COMANDOS
DIFERENTES ILUSTRE COMO FUNCIONAN LOS METODOS QUE SE UTILIZAN EN
CADA PROGRAMA

En Java es imprescindible que a la hora de la declaración de un método, se indique el tipo


de dato que ha de devolver. Si no devuelve ningún valor, se indicará el tipo void como
retorno.

Los métodos y funciones en C++ pueden devolver una variable u objeto, bien sea por
valor (se devuelve una copia), por puntero o por referencia. Java no soporta punteros, así
que no puede devolver nada por puntero. Todos los tipos primitivos en Java se devuelven
por valor y todos los objetos se devuelven por referencia. El retorno de la referencia a un
objeto en Java es similar a devolver un puntero a un objeto situado en memoria dinámica
en C++, excepto que la sintaxis es mucho más simple en Java, en donde el item que se
devuelve es la dirección de la posición en memoria dinámica donde se encuentra
almacenado el objeto.

Para devolver un valor se utiliza la palabra clave return. La palabra clave return va
seguida de una expresión que será evaluada para saber el valor de retorno. Esta
expresión puede ser compleja o puede ser simplemente el nombre de un objeto, una
variable de tipo primitivo o una constante.

El ejemplo java506.java ilustra el retorno por valor y por referencia.

// Un objeto de esta clase sera devuelto por referencia


class miClase {
int varInstancia = 10;
}

Si un programa Java devuelve una referencia a un objeto y esa referencia no es asignada


a ninguna variable, o utilizada en una expresión, el objeto se marca inmediatamente para
que el reciclador de memoria en su siguiente ejecución devuelve la memoria ocupada por
el objeto al sistema, asumiendo que la dirección no se encuentra ya almacenada en
ninguna otra variable. En C++, si un programa devuelve un puntero a un objeto situado en
memoria dinámica y el valor de ese puntero no se asigna a una variable, la posibilidad de
devolver la memoria al sistema se pierde y se producirá un memory leak, asumiendo que
la dirección no está ya disponible para almacenar ninguna otra variable.

Tanto en Java como en C++ el tipo del valor de retorno debe coincidir con el tipo de
retorno que se ha indicado en la declaración del método; aunque en Java, el tipo actual
de retorno puede ser una subclase del tipo que se ha indicado en la declaración del
método, lo cual no se permite en C++. En Java esto es posible porque todas las clases
heredan desde un objeto raíz común a todos ellos: Object.

En general, se permite almacenar una referencia a un objeto en una variable de


referencia que sea una superclase de ese objeto. También se puede utilizar un interfaz
como tipo de retorno, en cuyo caso, el objeto retornado debe implementar dicho interfaz.

Nombre del Método

El nombre del método puede ser cualquier identificador legal en Java. Java soporta el
concepto de sobrecarga de métodos, es decir, permite que dos métodos compartan el
mismo nombre pero con diferente lista de argumentos, de forma que el compilador pueda
diferenciar claramente cuando se invoca a uno o a otro, en función de los parámetros que
se utilicen en la llamada al método.

El siguiente fragmento de código muestra una clase Java con cuatro métodos
sobrecargados, el último no es legal porque tiene el mismo nombre y lista de argumentos
que otro previamente declarado:

class MiClase {
...
void miMetodo( int x,int y ) { . . . }
void miMetodo( int x ) { . . . }
void miMetodo( int x,float y ) { . . . }
// void miMetodo( int a,float b ) { . . . } // no válido
}

Todo lenguaje de programación orientado a objetos debe soportar las características de


encapsulación, herencia y polimorfismo. La sobrecarga de métodos es considerada por
algunos autores como polimorfismo en tiempo de compilación.

En C++, dos versiones sobrecargadas de una misma función pueden devolver tipos
diferentes. En Java, los métodos sobrecargados siempre deben devolver el mismo tipo.

Métodos de Instancia

Cuando se incluye un método en una definición de una clase Java sin utilizar la palabra
clave static, estamos generando un método de instancia. Aunque cada objeto de la clase
no contiene su propia copia de un método de instancia (no existen múltiples copias del
método en memoria), el resultado final es como si fuese así, como si cada objeto
dispusiese de su propia copia del método.

Cuando se invoca un método de instancia a través de un objeto determinado, si este


método referencia a variables de instancia de la clase, en realidad se están referenciando
variables de instancia específicas del objeto específico que se está invocando.

La llamada a los métodos de instancia en Java se realiza utilizando el nombre del objeto,
el operador punto y el nombre del método.

miObjeto.miMetodoDeInstancia();

En C++, se puede acceder de este mismo modo o utilizando una variable puntero que
apunte al objeto

miPunteroAlObjeto->miMetodoDeInstancia();

Los métodos de instancia tienen acceso tanto a las variables de instancia como a las
variables de clase, tanto en Java como en C++.
Métodos Estáticos

Cuando una función es incluida en una definición de clase C++, o un método e incluso en
una definición de una clase Java, y se utiliza la palabra static, se obtiene un método
estático o método de clase.

Lo más significativo de los métodos de clase es que pueden ser invocados sin necesidad
de que haya que instanciar ningún objeto de la clase. En Java se puede invocar un
método de clase utilizando el nombre de la clase, el operador punto y el nombre del
método.

MiClase.miMetodoDeClase();

En C++, hay que utilizar el operador de resolución de ámbito para poder invocar a un
método de clase:

MiClase::miMetodoDeClase();

En Java, los métodos de clase operan solamente como variables de clase; no tienen
acceso a variables de instancia de la clase, a no ser que se cree un nuevo objeto y se
acceda a las variables de instancia a través de ese objeto.

Si se observa el siguiente trozo de código de ejemplo:

class Documento extends Pagina {


static int version = 10;
int numero_de_capitulos;
static void annade_un_capitulo() {
numero_de_capitulos++; // esto no funciona
}
static void modifica_version( int i ) {
version++; // esto si funciona
}
}

la modificación de la variable numero_de_capitulos no funciona porque se está violando


una de las reglas de acceso al intentar acceder desde un método estático a una variable
no estática.

Todas las clases que se derivan, cuando se declaran estáticas, comparten la misma
página de variables; es decir, todos los objetos que se generen comparten la misma zona
de memoria. Los métodos estáticos se usan para acceder solamente a variables
estáticas.

class UnaClase {
int var;
UnaClase() {
var = 5;
}
unMetodo() {
var += 5;
}
}

En el código anterior, si se llama al método unMetodo() a través de un puntero a función,


no se podría acceder a var, porque al utilizar un puntero a función no se pasa
implícitamente el puntero al propio objeto (this). Sin embargo, sí se podría acceder a var
si fuese estática, porque siempre estaría en la misma posición de memoria para todos los
objetos que se creasen de la clase UnaClase.

Paso de parámetros

En C++, se puede declarar un método en una clase y definirlo luego dentro de la clase
(bajo ciertas condiciones) o definirlo fuera de la clase. A la hora de declararlo, es
necesario indicar el tipo de argumentos que necesita, pero no se requiere indicar sus
nombres (aunque pueda hacerse). A la hora de definir el método sí tiene que indicarse el
nombre de los argumentos que necesita el método.

En Java, todos los métodos deben estar declarados y definidos dentro de la clase, y hay
que indicar el tipo y nombre de los argumentos o parámetros que acepta. Los argumentos
son como variables locales declaradas en el cuerpo del método que están inicializadas al
valor que se pasa como parámetro en la invocación del método.

En Java, todos los argumentos de tipos primitivos deben pasarse por valor, mientras que
los objetos deben pasarse por referencia. Cuando se pasa un objeto por referencia, se
está pasando la dirección de memoria en la que se encuentra almacenado el objeto.

Si se modifica una variable que haya sido pasada por valor, no se modificará la variable
original que se haya utilizado para invocar al método, mientras que si se modifica una
variable pasada por referencia, la variable original del método de llamada se verá
afectada de los cambios que se produzcan en el método al que se le ha pasado como
argumento.

El ejemplo java515.java se ilustra el paso de parámetros de tipo primitivo y también el


paso de objetos, por valor y por referencia, respectivamente.

// Esta clase se usa para instanciar un objeto referencia


class MiClase {
int varInstancia = 100;
}

// Clase principal
class java515 {

// Función para ilustrar el paso de parámetros


void pasoVariables( int varPrim,MiClase varRef ) {
System.out.println( "--> Entrada en la funcion pasoVariables" );
System.out.println( "Valor de la variable primitiva: "+varPrim );
System.out.println( "Valor contenido en el objeto: "+
varRef.varInstancia );

System.out.println( "-> Modificamos los valores" );


varRef.varInstancia = 101;
varPrim = 201;
System.out.println( "--> Todavia en la funcion pasoVariables" );
System.out.println( "Valor de la variable primitiva: "+varPrim );
System.out.println( "Valor contenido en el objeto: "+
varRef.varInstancia );
}

public static void main( String args[] ) {


// Instanciamos un objeto para acceder a sus métodos
java515 aObj = new java515();

// Instanciamos un objeto normal


MiClase obj = new MiClase();
// Instanciamos una variable de tipo primitivo
int varPrim = 200;

System.out.println( "> Estamos en main()" );


System.out.println( "Valor de la variable primitiva: "+varPrim );
System.out.println( "Valor contenido en el objeto: "+
obj.varInstancia );
// Llamamos al método del objeto
aObj.pasoVariables( varPrim,obj );

System.out.println( "> Volvemos a main()" );


System.out.println( "Valor de la variable primitiva, todavia : "+
varPrim );
System.out.println( "Valor contenido ahora en el objeto: "+
obj.varInstancia );
}
}

En C++, se puede pasar como parámetro un puntero que apunte a una función dentro de
otra función, y utilizar este puntero en la segunda función para llamar a la primera. Esta
capacidad no está directamente soportada en Java. Sin embargo, en algunos casos, se
puede conseguir casi lo mismo encapsulando la primero función como un método de
instancia de un objeto y luego pasar el objeto a otro método, donde el primer método se
puede ejecutar a través del objeto.

Tanto en Java como en C++, los métodos tienen acceso directo a las variables miembro
de la clase. El nombre de un argumento puede tener el mismo nombre que una variable
miembro de la clase. En este caso, la variable local que resulta del argumento del
método, oculta a la variable miembro de la clase.

Cuando se instancia un método se pasa siempre una referencia al propio objeto que ha
llamado al método, es la referencia this.

Definición de métodos de una clase

Para definir los métodos se emplea la siguiente sintaxis:

[modifVisibilidad] [modifFunción] tipo nombreFunción (listaParámetros) [throws


listaExcepciones]
{

Para modifVisibilidad se aplica las mismas normas que para atributos:

public: indica que es un método accesible a través de una instancia del objeto.

private: indica que a través de una instancia no es accesible el método. Al heredar el


método se convierte en inaccesible.

protected: indica que a través de una instancia no es accesible el método. Al heredar si se


puede usar desde la clase derivada.

Sin especificar: indica visibilidad de paquete, se puede acceder a través de una instancia,
pero sólo de clases que se encuentren en el mismo paquete.

nombreFunc debe de ser un identificador válido en el lenguaje.

tipo es el tipo del valor devuelto por la función, pudiendo ser:

Un tipo básico.

Un objeto de una clase o interfaz. En este tipo de objetos se incluyen las matrices o
vectores.

void, en el caso de no devolver ningún valor.

Métodos

El comcepto de método consiste es una forma de encapsular un conjunto de


instrucciones dentro de una declaración específica (llamada generalmente
SUBPROGRAMA), permitiendo la descomposición funcional y la diferenciación de tareas.

La declaración de un método está formada por una cabecera y un cuerpo.

Estructuración en Java

La codificación de un método consiste en una cabecera para su identificación y de un


cuerpo que contiene las sentencias que éste ejecuta. La cabecera se compone de un
nombre (identificador del método), el tipo del resultado(tipos primitivos o clases) y una
lista de parámetros, que puede contener cero o más variables.

La lista de parámetros consiste en cero o más parámetros formales (variables), cada


uno de ellos con un tipo.
En caso que el método tenga más de un parámetro, estos deben ir separados con una
coma.
Llamada a un método

Los métodos pueden ser invocados o llamados de cualquier método de la clase, incluido
él mismo.

Además, cuando se invoca, hay que pasar un valor a cada paramétro, a través de una
variable o un valor constante. En Java, la acción de pasar valores a parámetros de tipo
primitivo (int, double, boolean, char..) se denomina paso de parámetros por valor
En éste caso, los argumentos que se pasan, no pueden ser modificados por la función.

En caso que el parámetro sea de tipo Clase o arreglo, lo que se está haciendo es un
paso de parámetros por referencia, y en este caso, los parámetros si pueden ser
modificados por el método

Cuerpo

El código que realmente implementa el método,llamado cuerpo del método ,es


formalmente un bloque de instrucciones.

Ejemplo

/* 1) */ int sumar(int a , int b)


/* 2) */ {
/* 3) */ return a + b;
/* 4) */ }

1) cabecera del método : int sumar(int a,int b)

tipo del resultado : int


nombre del método : sumar
lista de parámetros : int a,int b

2) comienzo del bloque o cuerpo del método

3) Instrucción : se retorna la suma de a y b

4) fin del bloque

Si se coloca las palabras public static antes del método se logra un comportamiento de
tipo global.

import java.io.*;
class suma
{
public static void main(String arg[ ]) throws IOException
{
int x,y;

BufferedReader in = new BufferedReader(new


InputStreamReader(System.in));

System.out.print("Ingrese un numero : ");


x = Integer.parseInt(in.readLine( ));

System.out.print("Ingrese un numero : ");


y = Integer.parseInt(in.readLine( ));

int s = suma(x,y);

System.out.println("La suma es : " + s);


}

public static int suma(int a,int b)


{
return a + b;
}

}
Bajar archivo

Salida a pantalla

Ejemplo de llamada a un método que determina si un número es par o impar

class metodo1
{
public static void main(String arg[ ])
{
int a = 5;

if ( par(a) == true)
{
System.out.println(a + " es par ");
}
else
{
System.out.println(a + " es impar");
}
}

public static boolean par(int num)


{
boolean p = false;
if (num % 2 == 0)
{
p = true;
}

return p;
}
}

Ejemplo

Tomando los algoritmos para invertir un numero especificados en la sección de


Manipulación numérica implementar un programa en java que determine los 100 primeros
números palíndromes a partir de un número ingresado por teclado en adelante, llamando
al método : invertir_num.

Código fuente

import java.io.*;
class palindromes
{
public static void main(String Arg[ ]) throws IOException
{
int numero = 0;
int contador = 0;

BufferedReader in = new BufferedReader(new


InputStreamReader(System.in));

System.out.print("Ingrese numero : ");


numero = Integer.parseInt(in.readLine( ));

System.out.println("\nLos 100 primeros palindromes a contar del " + numero);


System.out.println("\n");

while (contador < 100)


{
if ( invertir_num(numero) == numero)
{
System.out.print(numero + "\t");
contador++;
}

numero++;
}
}

public static int invertir_num ( int num )


{
int div_entera,resto,num_inv;

num_inv = 0;
div_entera = num;
resto = 0;

while (div_entera != 0)
{
resto = div_entera % 10;
div_entera = div_entera / 10;
num_inv = num_inv * 10 + resto;
}

return num_inv;
}
}
Bajar archivo

Ejercicios

1) Traducir a Java los siguientes algoritmos

a) Se quiere determinar cuantos y cuales números perfectos y pares, existen


entre a y b.

**nota : un numero perfecto es aquel que la suma de sus divisores sea igual
al numero
ej --> 6 : 1 + 2 + 3

Para ello el usuario, debe ingresar por teclado dos números enteros (a y b) e
implementar dos funciones :

par(num) : boolean
perfecto(num) : boolean

ALGORITMO
---------

FUNCION par(num : entero) : boolean


INICIO
SI (MOD(num/2) == 0) ENTONCES
RETORNAR true
SINO
RETORNAR false
FIN SI
FIN

FUNCION perfecto(num : entero) : boolean


INICIO
sum <-- 0
PARA i <-- 1 HASTA num - 1 HACER
SI(RESTO(num/i) == 0) ENTONCES
sum <-- sum + i
FIN SI
FIN PARA

SI (sum == num) ENTONCES


RETORNAR true
SINO
RETORNAR false
FIN SI

FIN

INICIO
cont <-- 0
leer a , b

PARA i <-- a HASTA b HACER


SI (par(i) == true y perfecto(i) == true) ENTONCES
escribir i
cont <-- cont + 1
FIN SI
FIN PARA

Escribir "Existen " + cont + " numeros que cumplen la condicion"

FIN

b) Se desea saber el valor de la siguiente sumatoria

s = SUMATORIA x^2 - y : desde a hasta b

siendo x : numero primo e y = numero multiplo de 5 o 7

Para ello se implemetaran tres funciones o métodos

sumatoria(a : entero, b : entero) : entero


primo(num : entero) : boolean
multiploEspecial(num : entero) : boolean
ALGORITMO
---------

FUNCION primo(num : entero) : boolean


INICIO
cont <-- 0
PARA i <-- 1 HASTA num - 1 HACER
SI (MOD(num/i) == 0) ENTONCES
cont <-- cont + 1
FIN SI
FIN PARA

SI (cont < 2) ENTONCES


RETORNAR true
SINO
RETORNAR false
FIN SI
FIN

FUNCION multiploEspecial(num : entero) : boolean


INICIO
aux <- false
SI (MOD(num/5) == 0 OR MOD(num/7) == 0 ) ENTONCES
aux <-- NOT aux
FIN SI
RETORNAR aux
FIN

FUNCION sumatoria(a : entero, b : entero) : entero


INICIO
sum <- 0
PARA i <- a HASTA b HACER
SI (primo(i) == true) ENTONCES
sum <-- sum + i * i
FIN SI

SI (multiploEspecial(i) == true) ENTONCES


sum <-- sum - i
FIN SI
FIN PARA

RETORNAR sum
FIN

INICIO
leer a , b
s <-- sumatoria(a,b)
escribir "El valor de la sumatoria de " + a + " hasta " + b + " es : " + s
FIN
9. EXPLIQUE AMPLIAMENTE, PARA ESCRIBIR CORRECTAMENTE LAS
EXPRESIONES QUE SE UTLIZAN PARA COMUNICARSE A TRAVES DE LOS
METODOS

El cuerpo de los métodos

Otra vez recordaremos nuestra vieja clase Contador:

// Implementación de un contador sencillo


public class Contador {
………………..
public int incCuenta() {
cnt++;
return cnt;
}
…………………
}

Dentro de los métodos pueden incluirse:

• Declaración de variables locales


• Asignaciones a variables
• Operaciones matemáticas
• Llamados a otros métodos:
o dentro de la clase
o de instancia, de otras clases
o de clase, de cualquier clase
• Estructuras de control
• Excepciones (try, catch, que veremos más adelante)

Declaración de variables locales

Las variables locales se declaran igual que los atributos de la clase:

Tipo NombreVariable [= Valor];

Ej: int suma;

float precio;

Contador laCuenta;

Sólo que aquí no se declaran private, public, etc., sino que las variables definidas dentro
del método sólo son accesibles por él.

Las variables pueden inicializarse al crearse:

Ej: int suma = 0;


float precio = 12.3;

Contador laCuenta = new Contador ( );

Asignaciones a variables

Se asigna un valor a una variable mediante el signo =:

Variable = Constante | Expresión ;

Ej: suma = suma + 1;

precio = 1.05 * precio;

laCuenta.cnt = 0;

El último caso es válido si cnt es una variable pública de la clase Contador.


Personalmente no creo conveniente acceder directamente a variables de otro objeto, ya
que futuras modificaciones del objeto llamado o del que llama puede propender la difusión
de errores… Es mejor usar métodos como getCuenta o un hipotético inicializarContador
para ello. De hecho, algunos sugieren que todas las variables de una clase se declaren
como private.

En el primer caso, o sea en general:

Variable = Variable Operador Expresión;

se puede escribir en forma más sencilla:

Variable Operador= Expresión;

Por ejemplo, suma = suma + 9 - cantidad;

puede escribirse: suma += 9-cantidad;

y precio = precio * 0.97;

como: precio *= 0.97;

Operaciones matemáticas

Hay varios tipos de operadores:

Unarios: + - ++ -- ~ ! (tipo) …..etc.

Se colocan antes (o en algunos casos después) de la constante o expresión.

Por ejemplo: -cnt; // cambia de signo; por ejemplo si cnt es 12 el resultado es -12; cnt no
cambia.
++cnt; // equivale a cnt += 1;

cnt++; // equivale a cnt +=1; veremos la diferencia al hablar de estructuras de control

--cnt; // equivale a cnt -= 1;

cnt--; // equivale a cnt -= 1;

Binarios: + - * / % …..etc.

Van entre dos constantes o expresiones o combinación de ambas.

Por ejemplo: cnt + 2; // debuelve la suma de ambos.

promedio + ( valor / 2); // como se ve, se pueden usar paréntesis.

horas / hombres; // división.

acumulado % 3; // resto de la división entera entre ambos.

Nota: + sirve también para concatenar cadenas de caracteres; hablaremos de String y


StringBuffer pronto. Cuando se mezclan Strings y valores numéricos, éstos se convierten
automáticamente a cadenas:

"La frase tiene " + cant + " letras"

se convierte en: "La frase tiene 17 letras" // suponiendo que cant = 17

Precedencia de operadores en Java

La siguiente es la precedencia de los operadores en expresiones compuestas. De todos


modos, como en todos los lenguajes, se recomienda usar paréntesis en caso de duda.

Posfijos [] . (params) expr++ expr--

Operadores unarios ++expr --expr +expr -expr ~ !

Creación y "cast" new (type)

Multiplicativos * / %

Aditivos + -

Desplazamiento << >> >>>

Relacionales < > <= >= instanceof

Igualdad == !=

AND bit a bit &


OR exclusivo bit a bit ^

OR inclusivo bit a bit |

AND lógico &&

OR lógico ||

Condicional ? :

Asignación = += -= *= /= %= ^= &= |= <<= >>= >>>=

Algunos ejemplos:

[] define arreglos: int lista[];

(params) es la lista de parámetros cuando se llama a un método: convertir(valor, base);

new permite crear una instancia de un objeto: new Contador();

(type) cambia el tipo de una expresión a otro: (float)(total % 10);

>> desplaza bit a bit un valor binario: base >> 3;

<= devuelve "true" si un valor es menor o igual que otro: total <= maximo;

instanceof devuelve "true" si el objeto es una instancia de la clase: papa instanceof


Comida;

|| devuelve "true" si cualquiera de las expresiones es verdad: (a<5) || (a>20)

Llamadas a métodos

Se llama a un método de la misma clase simplemente con el nombre del método y los
parámetros entre paréntesis, como se ve, entre otros, en el ejemplo en negrita:

// Archivo: Complejo.java
// Compilar con: javac Complejo.java
public final class Complejo extends Number {
// atributos:
private float x;
private float y;
// constructor:
public Complejo(float rx, float iy) {
x = rx;
y = iy;
}
// métodos:
public float Norma() {
return (float)Math.sqrt(x*x+y*y);
}
// obligatorios (son abstractos en Number):
public double doubleValue() {
return (double)Norma( );
}
public float floatValue() {
return Norma();
}
public int intValue() {
return (int)Norma();
}
public long longValue() {
return (long)Norma();
}
public String toString() {
return "("+x+")+i("+y+")";
}
}
Pueden probar la clase (mínima) con el siguiente ejemplo de aplicación; la línea en
negrita es un ejemplo de un llamado a un método de un objeto de otra clase. Notar que es
este caso, es necesario llamar al método sobre un objeto (instancia) existente, por lo que
se indica:

Nombre_del_Objeto<punto>Nombre_del_Método(parámetros)

// Archivo: Ejemplo4.java
// Compilar con: javac Ejemplo4.java
// Ejecutar con: java Ejemplo4
import java.io.*;

public class Ejemplo4 {


public static void main(String args[]) {
Complejo numComp = new Complejo(4,-3);
System.out.println(numComp.toString());
System.out.println(numComp.Norma());
}
}

En la clase Complejo tenemos también un ejemplo de un llamado a un método de clase, o


sea static:

return (float)Math.sqrt(x*x+y*y);

Como el método es de clase, no hace falta llamarlo para un objeto en particular. En ese
caso, en lugar del nombre de un objeto existente se puede utilizar directamente el nombre
de la clase:

Nombre_de_la_Clase<punto>Nombre_del_Método(parámetros)

Práctica

Continuamos con la clase Persona:


Vamos a crear 4 métodos que permitan acceder a los atributos privados de la clase
persona. Los agregamos dentro de la clase:

public int getEdad()

return edad;

public void setEdad(int laEdad)

edad = laEdad;

public String getNombre()

return nombre;

public void setNombre(String elNombre)

nombre = elNombre;

listaParámetros es la lista de los parámetros que tomará la función separados por comas
y definidos cada uno de ellos como:

tipo nombreParámetro

modifFunción puede tener los siguientes valores:

static: el método pertenece a la clase, no a los objetos creados a partir de la clase.

final: el método no puede ser sobrescrito en una clase derivada.

abstract: En esta clase no se proporciona el código para la función, se debe de


proporcionar en alguna clase derivada. En el caso de poseer un método abstracto la clase
debe de llevar a su vez el modificador abstract. En caso de ser abstracto un método, se
debe de sustituir las llaves que contienen el código por un punto y coma.
native: Es un método no escrito en java, sino en código nativo, que será usado en java
como un método propio de java.

synchronized: Es un método que sólo puede ser ejecutado por un hilo, y hasta que ese
hilo no acabe la llamada al método, no puede comenzar la llamada al método otro hilo. Lo
emplearemos al trabajar con hilos.

La cláusula opcional throws es empleada para indicar que dentro del método se pueden
generar errores en su ejecución, y que debemos estar preparados para tratarlos.

listaExcepciones es el nombre de todos esos posibles errores, su utilización la veremos


en el punto dedicado a la gestión de errores mediante try y catch.

El método posee un par de llaves, dentro de las cuales estará el código que se ejecutará
al ser llamada la función. Dicho código estará formado por instrucciones válidas en el
lenguaje, finalizadas generalmente por punto y coma.

10. TOMANDO EN CUENTA EL CONCEPTO DE PARAMETRO EXPLIQUE


AMPLIAMENTE COMO SE UTILIZA

Un parametro es una variable que puede pasar su valor a un procedimiento desde el


principal o desde otro procedimiento. Existen ocasiones en que es necesario mandar al
procedimiento ciertos valores para que los use en algún proceso. Estos valores que se
pasan del cuerpo principal del programa al procedimiento se llaman parametros.
Entonces una declaracion completa es: Static void Nom Proc?(lista de parametros)
{ cuerpo de instrucciones; }; import java.lang.*; import java.io.*; class prog13 { public static
void main(String[] args) {

// llamando procedimiento1 y pasando algunos parametros double beta=3.1416;


proc1(8+4 , beta, “Jesus” ); }; // cierra main public static void proc1(int a, double b, String
nom ) { double c = a + b; System.out.println(nom+ “ la suma fue =“ + c); }; // cierra proc } //
cierra clase

REGLAS PARA EL USO DE PARAMETROS

1.- Cuando se usan variables como parametros, la variable que se manda debe ser
declarada dentro del principal o del procedimiento de donde se esta enviando. 2.- La
variable que se manda tiene un nombre, la que se recibe puede tener otro nombre. 3.- La
cantidad de variables que se envian deben ser igual en cantidad, orden y tipo a las
variables que reciben. 4.- La variable que recibe tiene un ambito local dentro del
procedimiento, es decir solo la puede usar ese procedimiento. Y se pueden mandar datos,
valores (excepto decimales), expresiones algebraicas, pero siempre se recibe en
variables.
Los parametros de una función son los valores que esta recibe por parte del código que la
llama. Pueden ser tipos simples u objetos.

En la declaración de la función se escriben después del nombre entre parentesis


indicandose el tipo de cada uno y su nombre. Veamos esto a modo de ejempo:

int dividir(int dividendo, int divisor) {

Está función recibe dos parámetros, ambos de tipo entero, uno el divisor y otro el
dividendo.

A la hora de llamar a la función, los parametros se escriben también a continuación del


nombre entre parentesis; pero en este caso no se indica el tipo de estos. Veamoslo en un
ejemplo:

int resultado = dividir(8,4);

En esta linea, se asigna a la variable entera resultado el retorno de la función dividir


pasandole como parametros 8 y 4.

Es importante recordar que en java, los parametros de los tipos primitivos (int, long, etc.)
SIEMPRE se pasan por valor. Los objetos se pasan por referencia.

En el paso de parámetros a funciones hay dos aproximaciones clásicas: el paso por valor
y paso por referencia.

En el paso por valor se realiza una copia de los valores que se pasan, trabajando dentro
de la función con la copia. Es por ello que cualquier cambio que sufran dentro, no
repercute fuera de la función.

En el paso por referencia no se realiza dicha copia, por lo que las modificaciones de
dentro de las funciones afectan a los parámetros y esos cambios permanecen al final de
la función.

En Java el paso por parámetro es por valor, aunque los efectos son de paso por
referencia cuando los argumentos son objetos. ¿cómo sucede eso? Pues es muy fácil, si
una función tiene como argumento un tipo primitivo (int, float, etc...), en Java se realiza
una copia para la función y cualquier cambio a dicho argumento no afecta a la variable
original. Este paso de parámetros en Java está orientado a utilizar el valor de la variable
para otros cálculos.

En el caso de los objetos es distinto. En realidad lo que sucede es que en Java siempre
tenemos referencias a los objetos. Por eso al pasar a una función como argumento un
objeto, pasamos la referencia al mismo, es decir, aunque se hace una copia para el paso
por valor, como lo que se copia es una referencia, los cambios al objeto referenciado sí
son visibles y afectan fuera de la función.
La única excepción es la clase String , cuyos objetos no son mutables. Cualquier
modificación de un objeto String lleva aparejada la creación de una nueva instancia del
objeto. Si deseamos el mismo comportamiento para el paso de parámetros del resto de
objetos, tenemos que recurrir al objeto StringBuffer.

Un parametro es una variable que puede pasar su valor a un procedimiento desde el


principal o desde otro procedimiento.

Existen ocasiones en que es necesario mandar al procedimiento ciertos valores para que
los use en algún proceso.

Estos valores que se pasan del cuerpo principal del programa al procedimiento se llaman
parametros.

Entonces una declaracion completa es:

void NomProc(lista de parametros)

{ cuerpo de instrucciones; };

prog13.java

import java.io.*;

import javax.servlet.*;

import javax.servlet.http.*;

public class prog13 extends HttpServlet

PrintWriter pagina;

public void doGet (HttpServletRequest request,

HttpServletResponse response)

throws ServletException, IOException

pagina =response.getWriter();

response.setContentType("text/html");

pagina.println("<HTML>");

// llamando procedimiento1 y pasando algunos parametros

double beta=3.1416;
proc1(8+4 , beta, "juan" );

pagina.println("</HTML>");

pagina.close();

};

public void proc1(int a, double b, String nom )

double c = a + b;

pagina.println(nom+ " la suma fue =" + c);

};

public void destroy(){super.destroy();};

REGLAS PARA EL USO DE PARAMETROS

1.- Cuando se usan variables como parametros, la variable que se manda debe ser
declarada dentro del principal o del procedimiento de donde se esta enviando.

2.- La variable que se manda tiene un nombre, la que se recibe puede tener otro nombre.

3.- La cantidad de variables que se envian deben ser igual en cantidad, orden y tipo a las
variables que reciben.

4.- La variable que recibe tiene un ambito local dentro del procedimiento, es decir solo la
puede usar ese procedimiento.

Y se pueden mandar datos, valores(excepto decimales), expresiones algebraicas, pero


siempre se recibe en variables.

TAREAS PROGRAMACION JAVA SERVLET

Capturar el nombre y las 3 calificaciones en un procedimiento, calcular promedio en un


segundo, imprimir nombre y promedio en principal

Construir una tabla de multiplicar que el usuario indique captura y control de ciclo en el
principal, calculo en un procedimiento.

Construir un procedimiento que reciba un numero entero y que mande llamar a un


segundo procedimiento pasando el letrero “PAR O IMPAR”.
11.- TOMANDO EN CUENTA EL CONCEPTO DE PARAMETRO ILUSTRE 5 EJEMPLOS
DE SU UTILIZACION

Existe comúnmente la creencia errónea de que en Java es posible pasar parámetros por
referencia, y no es así. Java siempre pasa los parámetros por valor. Esta confusión se
da debido a que todas las variables de objeto son referencias a objetos [1]. En el libro
“The Java Programming Language” de Ken Arnold y James Gosling (autores de Java),
sección 2.6.1., tenemos la siguiente cita: “There is exactly one parameter passing mode in
Java - pass by value - and that helps keep things simple.” [2] (Existe un solo modo de
paso de parámetros en Java – paso por valor – y eso ayuda a mantener las cosas
simples.).

Antes de continuar, vamos a recordar cuáles son las definiciones de paso por valor y paso
por referencia: [3]:

Paso por valor significa que cuando un argumento se pasa a una función, la función
recibe una copia del valor original. Por lo tanto, si la función modifica el parámetro, sólo la
copia cambia y el valor original permanece intacto.

Paso por referencia significa que cuando un argumento se pasa a una función, la
función recibe la dirección de memoria del valor original, no la copia del valor. Por lo tanto,
si la función modifica el parámetro, el valor original en el código que llamó a la función
cambia.

Vamos a valernos de ejemplos para explicar el mecanismo con el que Java pasa
parámetros a los métodos.

Tenemos el siguiente programa Java:

1 public class ValorOReferencia {


2
3 private String param1 = new String();
4
5 /** Creates a new instance of PassValueOrReference */
6 public ValorOReferencia(String param1) {
7 this.setParam1(param1);
8}
9
10 public static void cambiarObjeto(ValorOReferencia objeto) {
11 objeto = new ValorOReferencia(”Este es un nuevo objeto.”);
12 System.out.println(”Luego de \”reasignar\” pass: ” + objeto);
13 }
14
15 public static void cambiarParam1(ValorOReferencia objeto) {
16 objeto.setParam1(”Este es un nuevo valor para param1.”);
17 }
18
19 public static void main(String[] args) {
20 ValorOReferencia pass =
21 new ValorOReferencia(”Objeto inicial.”);
22 System.out.println(”Entender que Java pasa parámetros por valor: “);
23 System.out.println(”Antes de modificar pass es: ” + pass);
24 ValorOReferencia.cambiarObjeto(pass);
25 System.out.println(”De vuelta en main pass es: ” + pass);
26 System.out.println(”Ahora vamos a cambiar sólo param1:”);
28 ValorOReferencia.cambiarParam1(pass);
29 System.out.println(”De seguro param 1 ha cambiado: ” + pass);
30 System.out.println(”Parece difícil, pero no lo es.”);
31 }
32
33 public String getParam1() {
34 return param1;
35 }
36
37 public void setParam1(String param1) {
38 this.param1 = param1;
39 }
40
41 public String toString() {
42 return “[param1 = " + this.getParam1() + "]“;
43 }
44
45 }

Remitámonos a línea 20. Declaramos una variable pass, de tipo ValorOReferencia, con
su único atributo param1 inicializado con el valor “Objeto inicial.”. En la línea 23,
presentamos el objeto en pantalla, y se muestra el valor con el que fue declarado.

Salida del programa:

Entender que Java pasa parámetros por valor:


Antes de modificar pass es: [param1 = Objeto inicial.]

Ahora, en la línea 24 pasamos nuestra variable pass al método cambiarObjeto, método


que tiene un parámetro formal de tipo ValorOReferencia. En dicho método, en la línea 11,
se realiza una asignación

objeto = new ValorOReferencia("Este es un nuevo objeto.");

, se presenta el objeto “modificado” y el control regresa al método main.

Salida del programa:

Luego de "reasignar" pass: [param1 = Este es un nuevo objeto.]

Suponiendo que el paso de parámetros en Java fuera por referencia, la referencia pass
apuntaría ahora a un nuevo objeto con el valor “Este es un nuevo objeto.”. Pero, al
regresar al método main, en la línea 25, presentamos de nuevo pass, y vemos que el
valor con el que fue originalmente declarado se mantiene.

Salida del programa


De vuelta en main() pass es: [param1 = Objeto inicial.]

Ahora, vamos a pasar pass y vamos a modificar solamente su único atributo. En la línea
28, pasamos pass al método cambiarParam1, en donde tenemos la sentencia

objeto.setParam1("Este es un nuevo valor para param1.");

en la línea 16. Así, se ha modificado el valor del atributo param1, y al volver al método
main, presentamos pass otra vez:

Salida del programa:

Ahora vamos a cambiar sólo param1:


De seguro param 1 ha cambiado: [param1 = Este es un nuevo valor para param1.]
Parece difícil, pero no lo es.

Al ver esta última operación, quizá alguien pueda decir que Java sí pasa parámetros por
referencia, ya que se modificó el atributo del objeto, pero estaría equivocado: ¿Por qué en
cambiarObjeto la variable pass no sufre ninguna modificación, y en el método
cambiarParam1 su atributo se ve efectivamente modificado? Porque Java no pasa
objetos como parámetros [4], sino copias de las referencias a esos objetos. Exacto.
Java pasa parámetros por valor. Pasa referencias a objetos por valor.

Vamos a explicar lo que hacen nuestros métodos cambiarObjeto(ValorOReferencia


objeto) y cambiarParam1(ValorOReferencia objeto).

cambiarObjeto(ValorOReferencia objeto)

En main, declaramos una variable

ValorOReferencia pass = new ValorOReferencia("Objeto inicial.");

Se ha creado un objeto ValorOReferencia en cierta posición de memoria y la forma de


acceder a él es usar la referencia pass.

Omito los métodos setters, getters y toString() en el diagrama porque no intervienen en la


explicación de este método. Este método tiene un parámetro formal ValorOReferencia
objeto. Como Java pasa parámetros por valor tenemos que objeto, el parámetro formal de
cambiarObjeto, es una copia de la referencia pass, es un alias, mas no es la referencia
pass. Siguiente a la llamada al método lo que tenemos es lo siguiente:
objeto es una copia de la referencia pass, es otra referencia que apunta al mismo lugar. Al
ejecutar la sentencia

objeto = new ValorOReferencia("Este es un nuevo objeto.");

lo que hacemos de hecho es esto:

objeto, que originalmente era una copia de la referencia pass, apunta ahora a un nuevo
objeto creado en otra posición de memoria. Es por eso que de vuelta al main, el objeto
apuntado por pass no ha cambiado.

cambiarParam1(ValorOReferencia objeto)

Desde el método main pasamos a cambiarParam1 la referencia pass:

Incluyo el método setter setParam1(String param1) porque interviene en esta explicación.


cambiarParam1 tiene un parámetro formal ValorOReferencia objeto, que es un alias de
pass.
Al ejecutar la sentencia

objeto.setParam1("Este es un nuevo valor para param1.");

, lo que estamos haciendo es invocar al método setParam1 del objeto apuntado por la
referencia objeto. Es por eso que el atributo param1 del objeto referenciado por pass es
efectivamente modificado.

Cuando declaramos variables, por ejemplo,

ValorOReferencia valoRef = new ValorOReferencia();

, no declaramos un objeto ValorOReferencia, sino una referencia a un objeto


ValorOReferencia. Java tiene punteros (referencias a objetos), pero no es posible
manejarlos con la aritmética con que se manejan en C++ [5]. No obstante, la
implementación de estos punteros es similar en ambos lenguajes:

• Una sentencia Java ValorOReferencia valORef; es exactamente igual a una


sentencia C++ ValorOReferencia *valORef;.
• Una sentencia Java valORef.cambiarParam1(“Otro valor.”); es exactamente igual a
una sentencia C++ valORef -> cambiarParam1(“Otro valor.”);. [6]

Hay que tener claro entonces, que cuando se escribe una sentencia del estilo

cualquierFuncion(CualquierTipo argumento);

en Java, lo que se pasa no es un objeto, sino una copia de la referencia al objeto. Es


importante no confundirnos. Java pasa objetos por referencia (pasa las referencias a los
objetos), pero nunca pasa un objeto como parámetro [7], ni parámetros por referencia.

12. DEFINA EL CONCEPTO CONSTRUCTOR Y EXPLIQUE AMPLIAMENTE PARA


QUE SIRVE DENTRO DE UN PROGRAMA

CONSTRUCTORES (EN JAVA)

Un constructor es un método especial dentro de una clase, que se llama


automáticamente cada vez que se crea un objeto de esa clase.

Posee el mismo nombre de la clase a la cual pertenece y no puede regresar ningún valor
(ni siquiera se puede especificar la palabra reservada void). Por ejemplo si añadiéramos a
la clase SSuma un constructor, tendríamos que llamarlo también SSuma. Cuando en una
clase no se escribe propiamente un constructor, Java asume uno por default.
Un constructor por default es un constructor sin parámetros que no hace nada. Sin
embargo será invocado cada vez que se construya un objeto sin especificar ningún
argumento, en cuyo caso el objeto será iniciado con los valores predeterminados por el
sistema (los atributos numéricos a ceros, los alfanuméricos a nulos, y las referencias a
objetos a null).

Un constructor por default, para la clase SSuma quedaría así:

public SSuma() {}

Como se observa el constructor no posee ningún parámetro, ya que como no ha sido


definido propiamente por el programador, Java lo hace por default. Si se define un
constructor con parámetros,(definido explícitamente) el constructor por default se
reemplaza por éste. Ahora podemos crear un constructor explicito para una clase simple,
utilizaré el nombre Arychan para mi clase. Arychan es una clase que se refiere a una
persona de cierta edad, que posee un nombre y ciertos atributos, puede ser divertida, y
hermosa, estos atributos serán del tipo cadena de caracteres (String) por lo que agregaré
un atributo mas llamado salario y será de tipo numérico. Primero declaro la clase con sus
atributos:

class Arychan
{
//ATRIBUTOS
private String nombre;
private String descripción;
private String formaDeSer;
private double salario;

//METODOS

public Arychan() {} //CONSTRUCTOR SIN PARÁMETROS


public Arychan(String nom, String des, String forma, double sal)
{

asignarNombre(nom); // nombre = nom;


asignarDescripcion(des); // descripción = des;
describir(forma); //formaDeSer = forma;
ingreso(sal); //salario = sal;
}

//...

El constructor sin parámetros es reemplazado por el constructor explicito. Es este ejemplo


se puede observar que los constructores preferentemente se declaran públicos para que
puedan ser invocados desde cualquier parte.

una línea como esta invocará al constructor sin parámetros:


Arychan ary01 = new Arychan();// invoca al constructor Arychan

El operador new crea un nuevo objeto, en este caso de la clase Arychan, y a continuación
se invoca al constructor de la clase para realizar las operaciones de iniciación que estén
programadas. Ahora invocaremos al constructor con parámetros, recordemos que la clase
Arychan es una persona con características como divertida, hermosa, mismas que
pasaremos como argumentos.

Arychan ary02 = new Arychan("Ary", "hermosa", "divertida", 25000);

13. MEDIANTE 5 EJEMPLOS DE PROGRAMACION SEÑALE Y EXPLIQUE EL O LOS


CONSTRUCTOR UTILIZADOS

Código Fuente Arboles.java.

public class Arboles {

public Arboles() {
System.out.println("Un árbol genérico");
}

public Arboles(String tipo) {


System.out.println("Un árbol tipo " + tipo);
}

public Arboles(int altura) {


System.out.println("Un árbol de " + altura + " metros");
}

public Arboles(int altura,String tipo) {


System.out.println("Un " + tipo + " de " + altura + " metros");
}

public static void main(String args[]) {


Arboles arbol1 = new Arboles(4);
Arboles arbol2 = new Arboles("Roble");
Arboles arbol3 = new Arboles();
Arboles arbol4 = new Arboles(5,"Pino");

Clase Arboles
• Como en todo programa Java , primeramente se define la Clase a través del
vocablo class.

• Posteriormente son definidos 4 Constructores; nótese que cada uno recibe el


mismo nombre de la Clase y posee distintos argumentos de entrada.

• Dentro del método principal (main) son generadas cuatro instancias de la Clase,
como se puede observar, al ser generada la instancia a través del vocablo new se
pasa un parámetro, y es dependiendo de este parámetro que es llamado el
Constructor correspondiente, el cual a su vez invoca la Clase System.out.println
que imprime a pantalla.

Constructor Obligatorio...
En los ejemplos anteriores del curso se pudo notar que no se hizo uso de Constructor
alguno, y la razón es que el compilador lleva acabo esta definición de Constructor
vacío detrás de los escenarios, sin embargo, existe una situación en la que es
necesario definir un Constructor vacío y esta es cuando se hace uso de otros
constructores.

Lo anterior significa que si el compilador observa un método con el mismo nombre de


la clase con argumentos (Constructor), deberá existir un método vacío por el mismo
nombre, esto de cierta manera salvaguarda a un programador al momento de definir
métodos que no vayan a ser definidos erróneamente como Constructores

Tanto Java como C++ soportan la sobrecarga de métodos, es decir, que dos o más
métodos puedan tener el mismo nombre, pero distinta lista de argumentos en su
invocación. Si se sobrecarga un método, el compilador determinará ya en tiempo de
compilación, en base a lista de argumentos con que se llame al método, cual es la versión
del método que debe utilizar.

Tanto Java como C++ soportan la noción de constructor. El constructor es un tipo


específico de método que siempre tiene el mismo nombre que la clase y se utiliza para
construir objetos de esa clase. No tiene tipo de dato específico de retorno, ni siquiera
void. Esto se debe a que el tipo específico que debe devolver un constructor de clase es
el propio tipo de la clase.

En este caso, pues, no se puede especificar un tipo de retorno, ni se puede colocar


ninguna sentencia que devuelva un valor. Los constructores pueden sobrecargarse, y
aunque puedan contener código, su función primordial es inicializar el nuevo objeto que
se instancia de la clase. En C++, el constructor se invoca automáticamente a la hora de
crear un objeto. En Java, ha de hacerse una llamada explícita al constructor para
instanciar un nuevo objeto.

Cuando se declara una clase en Java, se pueden declarar uno o más constructores
opcionales que realizan la inicialización cuando se instancia (se crea una ocurrencia) un
objeto de dicha clase.

Utilizando el código de la sección anterior, cuando se crea una nueva instancia de


MiClase, se crean (instancias) todos los métodos y variables, y se llama al constructor de
la clase:

MiClase mc;
mc = new MiClase();
La palabra clave new se usa para crear una instancia de la clase. Antes de ser
instanciada con new no consume memoria, simplemente es una declaración de tipo.
Después de ser instanciado un nuevo objeto mc, el valor de i en el objeto mc será igual a
10. Se puede referenciar la variable (de instancia) i con el nombre del objeto:

mc.i++; // incrementa la instancia de i de mc

Al tener mc todas las variables y métodos de MiClase, se puede usar la primera sintaxis
para llamar al método Suma_a_i() utilizando el nuevo nombre de clase mc:

mc.Suma_a_i( 10 );

y ahora la variable mc.i vale 21.

Luego, en Java, cuando se instancia un objeto, siempre se hace una llamada directa al
constructor como argumento del operador new. Este operador se encarga de que el
sistema proporcione memoria para contener al objeto que se va a crear.

En C++, los objetos pueden instanciarse de diferentes formas, pero en Java solamente se
pueden instanciar en la pila de memoria, es decir, solamente se pueden instanciar
utilizando el operador new para poder solicitar memoria al sistema en tiempo de ejecución
y utilizar el constructor para instanciar el objeto en esa zona de memoria. Si al intentar
instanciar un objeto, la Máquina Virtual Java (JVM) no puede localizar la memoria que
necesita ese objeto, bien inmediatamente o haciendo ejecutarse al reciclador de memoria,
el sistema lanzará un OutOfMemoryError.

Tanto en Java como en C++, si no se proporciona explícitamente un constructor, el


sistema proporciona uno por defecto que inicializará automáticamente todas las variables
miembro a cero o su equivalente, en Java. En C++, el constructor de defecto no realiza
ningún tipo de inicialización.

Se puede pensar en el constructor de defecto en Java como un método que tiene el


mismo nombre que la clase y una lista de argumentos vacía. Y en C++, el constructor de
defecto sería como la llamada cuando se instancia un objeto sin parámetros

MiClase objeto;

En ambos lenguajes, si se proporciona uno o más constructores, el constructor de defecto


no se proporciona automáticamente y si fuese necesaria su utilización, tendría que
proporcionarlo explícitamente el programa.

Las dos sentencias siguientes muestran cómo se utiliza el constructor en Java para
declarar, instanciar y, opcionalmente, inicializar un objeto:

MiClase miObjeto = new MiClase();


MiClase miObjeto = new MiClase( 1,2,3 );

Las dos sentencias devuelven una referencia al nuevo objeto que es almacenada en la
variable miObjeto. También se puede invocar al constructor sin asignar la referencia a una
variable. Esto es útil cuando un método requiere un objeto de un tipo determinado como
argumento, ya que se puede incluir una llamada al constructor de este objeto en la
llamada al método:
miMetodo( new MiConstructor( 1,2,3 ) );

Aquí se instancia e inicializa un objeto y se pasa a la función. Para que el programa


compile adecuadamente, debe existir una versión de la función que espere recibir un
objeto de ese tipo como parámetro.

Tanto en Java como en C++, cuando un método o una función comienza su ejecución,
todos los parámetros se crean como variables locales automáticas. En este caso, el
objeto es instanciado en conjunción con la llamada a la función que será utilizada para
inicializar esas variables locales cuando comience la ejecución y luego serán guardadas.
Como son automáticas, cuando el método concluye su ejecución, se destruirá (en C++) o
será marcado para su destrucción (en Java).

En el siguiente ejemplo, java507.java, se ilustran algunos de los conceptos sobre


constructores que se han planteado en esta sección.

class MiClase {
int varInstancia;

// Este es el constructor parametrizado


MiClase( int dato ) {
// rellenamos la variable de instancia con los datos
// que se pasan al constructor
varInstancia = dato;
}

void verVarInstancia() {
System.out.println( "El Objeto contiene " + varInstancia );
}
}

class java507 {
public static void main( String args[] ) {
System.out.println( "Lanzando la aplicacion" );
// Instanciamos un objeto de este tipo llamando al
// constructor de defecto
java507 obj = new java507();
// Llamamos a la funcion pasandole un constructor
// parametrizado como parametro
obj.miFuncion( new MiClase( 100 ) );
}

// Esta funcion recibe un objeto y llama a uno de sus metodos


// para presentar en pantalla el dato que contiene el objeto
void miFuncion( MiClase objeto){
objeto.verVarInstancia();
}
}
Herencia

En casos en que se vea involucrada la herencia, los constructores toman un significado


especial porque lo normal es que la subclase necesite que se ejecute el constructor de la
superclase antes que su propio constructor, para que se inicialicen correctamente
aquellas variables que deriven de la superclase. En C++ y Java, la sintaxis para conseguir
esto es sencilla y consiste en incluir en el cuerpo del constructor de la subclase como
primera línea de código la siguiente sentencia:

super( parametros_opcionales );

Esto hará que se ejecute el constructor de la superclase, utilizando los parámetros que se
pasen para la inicialización. En el código del ejemplo siguiente, java508.java, se ilustra el
uso de esta palabra clase para llamar al constructor de la superclase desde una subclase.

class SuperClase {
int varInstancia;

// Es necesario proporcionar el constructor por defecto,que


// es aquel que no tiene parametros de llamada
SuperClase(){}

// Este es el constructor parametrizado de la superclase


SuperClase( int pDato ) {
System.out.println(
"Dentro del constructor de la SuperClase" );
varInstancia = pDato;
}

void verVarInstancia() {
System.out.println( "El Objeto contiene " + varInstancia );
}
}

class SubClase extends SuperClase {


// Este es el constructor parametrizado de la subclase
SubClase( int bDato ) {
// La siguiente sentencia println no compila, la llamada
// a super() debe estar al principio de un metodo en caso de
// que aparezca
// System.out.println( "En el constructor de la SubClase" );

// Llamamos al constructor de la superclase


super( bDato );
System.out.println(
"Dentro del constructor de la SubClase" );
}
}

class java508 {
public static void main( String args[] ) {
System.out.println( "Lanzando la aplicacion" );
// Instanciamos un objeto de este tipo llamando al
// constructor de defecto
java508 obj = new java508();
// Llamamos a la funcion pasandole un constructor de la
// subclase parametrizado como parametro
obj.miFuncion( new SubClase( 100 ) );
}

// Esta funcion recibe un objeto y llama a uno de sus metodos


// para presentar en pantalla el dato que contiene el objeto,
// en este caso el metodo es heredado de la SuperClase
void miFuncion( SubClase objeto ) {
objeto.verVarInstancia();
}
}

Si super no aparece como primera sentencia del cuerpo de un constructor, el compilador


Java inserta una llamada implícita, super(), al constructor de la superclase inmediata. Es
decir, el constructor por defecto de la superclase es invocado automáticamente cuando se
ejecuta el constructor para una nueva subclase, si no se especifica un constructor
parametrizado para llamar al constructor de la superclase.

Control de Acceso

El control de acceso también tiene un significado especial cuando se trata de


constructores. Aunque en otra sección se trata a fondo el tela del control de acceso en
Java, con referencia a los constructores se puede decir que el control de acceso que se
indique determina la forma en que otros objetos van a pode instanciar objetos de la clase.
En la siguiente descripción, se indica cómo se trata el control de acceso cuando se tienen
entre manos a los constructores:

private

Ninguna otra clase puede instanciar objetos de la clase. La clase puede contener
métodos públicos, y estos métodos pueden construir un objeto y devolverlo, pero nadie
más puede hacerlo.

protected

Solamente las subclases de la clase pueden crear instancias de ella.

public

Cualquier otra clase puede crear instancias de la clase.

package

Nadie desde fuera del paquete puede construir una instancia de la clase. Esto es útil si se
quiere tener acceso a las clases del paquete para crear instancias de la clase, pero que
nadie más pueda hacerlo, con lo cual se restringe quien puede crear instancias de la
clase.
En Java y en C++, una instancia de una clase, un objeto, contiene todas las variables y
métodos de instancia de la clase y de todas sus superclases. Sin embargo, los dos
lenguajes soportan la posibilidad de sobreescribir un método declarado en una
superclase, indicando el mismo nombre y misma lista de argumentos; aunque los
procedimientos para llevar a cabo esto son totalmente diferentes en Java y en C++.

Como aclaración a terminología que se empleo en este documento, quiero indicar que
cuando digo sobrecargar métodos, quiero decir que Java requiere que los dos métodos
tengan el mismo nombre, devuelvan el mismo tipo, pero tienen una diferente lista de
argumentos. Y cuando digo sobreescribir métodos, quiero decir que Java requiere que los
dos métodos tengan el mismo nombre, mismo tipo de retorno y misma lista de
argumentos de llamada.

En Java, si una clase define un método con el mismo nombre, mismo tipo de retorno y
misma lista de argumentos que un método de una superclase, el nuevo método
sobreescribirá al método de la superclase, utilizándose en todos los objetos que se creen
en donde se vea involucrado el tipo de la subclase que sobreescribe el método.

Finalizadores

Java no utiliza destructores (al contrario que C++) ya que tiene una forma de recoger
automáticamente todos los objetos que se salen del alcance. No obstante proporciona un
método que, cuando se especifique en el código de la clase, el reciclador de memoria
(garbage collector) llamará:

// Cierra el canal cuando este objeto es reciclado


protected void finalize() {
close();
}

Cada objeto tiene el método finalize(), que es heredado de la clase Object. Si se


necesitase realizar alguna limpieza asociada con la memoria, se puede sobreescribir el
método finalize() y colocar en él el código que sea necesario.

Los programadores C++ deben tener en cuenta que el método finalize() no es un


destructor. En C++, si existe un destructor, éste será invocado siempre que el objeto se
salga de ámbito o vaya a ser destruido. En Java, aunque el método finalize() siempre se
invocará antes de que el reciclador de memoria libere la zona de memoria ocupada por el
objeto, no hay garantía alguna de que el reciclador de memoria reclame la memoria de un
determinado objeto, es decir, no hay garantía de que el método finalize() sea invocado.

La regla de oro a seguir es que no se debe poner ningún código que deba ser ejecutado
en el método finalize(). Por ejemplo, si se necesita concluir la comunicación con un
servidor cuando ya no se va a usar un objeto, no debe ponerse el código de desconexión
en el método finalize(), porque puede que nunca se llamado. Luego, en Java, es
responsabilidad del programador escribir métodos para realizar limpieza que no involucre
a la memoria ocupada por el objeto y ejecutarlos en el instante preciso. El método
finalize() y el reciclador de memoria son útiles para liberar la memoria de la pila y debería
restringirse su uso solamente a eso, y no depender de ellos para realizar ningún otro tipo
de limpieza.
No obstante, Java dispone de dos métodos para asegurar que los finalizadores se
ejecuten. Los dos métodos habilitan la finalización a la salida de la aplicación, haciendo
que los finalizadores de todos los objetos que tengan finalizador y que todavía no hayan
sido invocados automáticamente, se ejecuten antes de que la Máquina Virtual Java
concluya la ejecución de la aplicación. Estos dos métodos son:

runFinalizersOnExit( boolean ), método estático de java.lang.Runtime, y

runFinalizersOnExit( boolean ), método estático de java.lang.System

Una clase también hereda de sus superclase el método finalize(), y en caso necesario,
debe llamarse una vez que el método finalize() de la clase haya realizado las tareas que
se le hayan encomendado, de la forma:

super.finalize();

En la construcción de un objeto, se desplaza uno por el árbol de jerarquía, de herencia,


desde la raíz del árbol hacia las ramas, y en la finalización, es al revés, los
desplazamientos por la herencia debe ser desde las ramas hacia las superclases hasta
llegar a la clase raíz.

Noción de constructor

Cuando se crea un objeto (se instancia una clase) es posible definir un proceso de
inicialización que prepare el objeto para ser usado. Esta inicialización se lleva a cabo
invocando un método especial denominado constructor. Esta invocación es implícita y se
realiza automáticamente cuando se utiliza el operador new. Los constructores tienen
algunas características especiales:

• El nombre del constructor tiene que se igual al de la clase.


• Puede recibir cualquier número de argumentos de cualquier tipo, como cualquier
otro método.
• No devuelve ningún valor (en su declaración no se declara ni siquiera void).

El constructor no es un miembro más de una clase. Sólo es invocado cuando se crea el


objeto (con el operador new). No puede invocarse explicitamente en ningún otro
momento.

Continuando con los ejemplos del capítulo anterior se podría escribir un constructor para
la clase Punto, de la siguiente forma:

class Punto {
int x , y ;
Punto ( int a , int b ) {
x=a;y=b;
}
}

Con este constructor se crearía un objeto de la clase Punto de la siguiente forma:

Punto p = new Punto ( 1 , 2 );


Constructor no-args.

Si una clase no declara ningún constructor, Java incorpora un constructor por defecto
(denominado constructor no-args) que no recibe ningún argumento y no hace nada.

Si se declara algún constructor, entonces ya no se puede usar el constructor no-args. Es


necesario usar el constructor declarado en la clase.

En el ejemplo el constructor no-args sería:

class Punto {
int x , y ;
Punto ( ) { }
}

Sobrecarga de constructores.

Una clase puede definir varios constructores (un objeto puede inicializarse de varias
formas). Para cada instanciación se usa el que coincide en número y tipo de argumentos.
Si no hay ninguno coincidente se produce un error en tiempo de compilación.

Por ejemplo:

class Punto {
int x , y ;
Punto ( int a , int b ) {
x=a;y=b;
}
Punto () {
x = 0 ; y = 0;
}
}

En el ejemplo se definen dos constructores. El citado en el ejemplo anterior y un segundo


que no recibe argumentos e inicializa las variables miembro a 0. (Nota: veremos más
adelante que este tipo de inicialización es innecesario, pero para nuestro ejemplo sirve).

Desde un constructor puede invocarse explicitamente a otro constructor utilizando la


palabra reservada this . this es una referencia al propio objeto. Cuando this es seguido
por parénteses se entiende que se está invocando al constructor del objeto en cuestión.
Puedes ver el uso más habitual de this aquí. El ejemplo anterior puede reescribirse de la
siguiente forma:

class Punto {
int x , y ;
Punto ( int a , int b ) {
x=a;y=b;
}
Punto () {
this (0,0);
}
}
Cuando se declaran varios constructores para una misma clase estos deben distinguirse
en la lista de argumentos, bien en el número, bien en el tipo.

Constructores

• Definicion: El constructor de una clase es un método estándar para inicializar los


objetos de esa clase

Se invoca automáticamente cuando new crea un objeto de esa clase.

• Los constructores se declaran en el momento de definir la clase.


• class A {
• int x, y;
• A() { x=0; y=0; } // el constructor
• ...
• }

• A a= new A();
• a.Print(); // 0 0
• El constructor puede tener parámetros. En este caso, se deben colocar los
argumentos respectivos al crear el objeto:
• class A {
• int x, y;
• A(int ix, int iy)
• { x=ix; y=iy; } // el constructor
• ...
• }

• A a= new A(1,2);
• a.Print(); // 1 2

• a= new A(); // error, hay que colocar
• // los argumentos
• a.A(1,2); // error, no se puede
• // invocar el constructor
• Se pueden colocar varios constructores. Durante la creación de un objeto, se
invoca aquel que calza con los argumentos dados:
• class A {
• int x, y;
• A() { x=0; y= 0; }
• A(int ix, int iy)
• { x=ix; y=iy; }
• A(A from)
• { x= from.x; y= from.y; }
• ...
• }

• A a1= new A();
• a1.Print(); // 0 0
• A a2= new A(1,2);
• a2.Print(); // 1 2
• A a3= new A(a2);
• a3.Print(); // 1 2
• Un destructor es un método que se invoca automáticamente cuando el objeto se
destruye. Java no posee destructores, porque tiene recolección de basuras. C++
posee destructores.

14. DEFINA EL CONCEPTO DESTRUCTOR Y EXPLIQUE AMPLIAMENTE PARA QUE


SIRVE DENTRO DE UN PROGRAMA

En esta sección abordaremos los conceptos básicos - iniciales, de la Programación


Orientada a Objetos, (POO). Se verán algunos conceptos como clases, polimorfismo,
herencia, etc., directamente con ejemplos, sin utilizar muchas definiciones "académicas",
tan sólo ejemplos.

Clases:

En lenguaje C tradicional existen las estructuras de datos, las cuales se definen con la
palabra clave struct, ejemplo:

struct Coordenadas
{
int x;
int y;
int z;
}

Con una estructura uno crea un tipo de dato nuevo, en este caso, se puede declarar una
variable de tipo Coordenadas, la cual puede almacenar 3 valores enteros:

struct Coordenadas coo; //Declaración de la variable coo de tipo Coordenadas


coo.x=7; //Valores iniciales para los datos miembros.
coo.y=15;
coo.z=55;

x, y, z son los "datos miembros" de la estructura. Para manipular estos datos, (asignarles
un valor inicial, cargarlos, mostrarlos, etc.), uno puede escribir funciones globales en su
programa. Ejemplo:

void Carga(void)
void Muestra(void)

Bueno, se podría decir que una estructura es el "antepasado" más directo de una clase.
¿Por qué?.
Que tal si las funciones con las cuales uno manipula los datos de la estructura formaran
parte de ella, o sea, una estructura tal que además de definir sus datos miembros también
definiera las funciones para manipularlos. Este tipo de estructuras existe en C++ y se
definen igual que las estructuras de C pero además uno puede declarar las funciones.
Mire el siguiente ejemplo: (para estos ejemplos puede usar Visual C++ o Borland C++ 3.1,
con cualquiera de ellos funcionan).

//Estructura con funciones miembros.


//Autor: Demian C. Panello.
#include <iostream.h>

struct Coordenadas
{
int x,y,z;

void Cargar(void) //Función miembro que carga los datos.


{
x=8;
y=9;
z=10;
}
void Mostrar(void) //Función miembro que muestra el contenido de los datos.
{
cout << x <<endl;
cout << y <<endl;
cout << z <<endl;
}
};

void main(void)
{
struct Coordenadas coo; //Se define una variable, (coo), de tipo Coordenadas.

coo.Cargar(); //Llamadas a las funciones de coo.


coo.Mostrar();
}

Ahora examine el siguiente programa y encuentre las diferencias con el anterior:

//Lo mismo pero con una clase.


//Autor: Demian C. Panello.
#include <iostream.h>

class Coordenadas
{
int x,y,z;

public:
void Cargar(void)
{
x=8;
y=9;
z=10;
}
void Mostrar(void)
{
cout << x <<endl;
cout << y <<endl;
cout << z <<endl;
}
};

void main(void)
{
Coordenadas coo;

coo.Cargar();
coo.Mostrar();

¿Encontró las diferencias?.


La verdad, no son muchas. En lugar de struct se pone class, luego se agrega la etiqueta
public, antes de definir las funciones miembros, ya que para una estructura los datos
miembros y funciones miembros son por defecto públicos, pero en una clase por
defecto los datos miembros son privados, (esto forma parte, entre otras cosas, de lo que
se llama "encapsular"), y sólo las funciones públicas pueden tener acceso a los datos
privados.
Y la otra diferencia es en el momento de definir(*) la variable coo, no hace falta especificar
la palabra class así como se hizo con struct.

(*) En la POO, utilizando clases, ya no se habla de "definir" una variable de una clase en
particular, sino que se crea una "instancia" o un objeto de dicha clase.

¿Por qué usar clases y no estructuras?.

A veces la diferencia, aparte de la sintaxis, no es del todo "pesada" como para justificar
una clase. En este ejemplo no hacía falta definir una clase, la versión de la estructura es
más que suficiente.
Pero cuando el concepto del objeto a crear es un tanto más complejo, y preocupa, por
ejemplo, la protección de los contenidos de los datos miembros, o se tiene una gran
cantidad de funciones miembros, o simplemente se pretende en serio programar según
POO, es cuando una clase se hace presente.
Pues como supongo astutamente dedujo, la Programación Orientada a Objetos, consta
de objetos, y una clase, define o es como la "plantilla" sobre la cual se construyen los tan
mentados.

Constructores:

En una clase existe una función miembro muy particular llamada Constructor.
Un constructor es una función que debe tener el mismo nombre que la clase y no debe
retornar ningún valor, (ni siquiera void), y se encarga de asignarle valores iniciales, (o
simplemente inicializar), a los datos miembros.
En el ejemplo descubrirá que allí no hay ningún constructor definido, cuando ocurre esto
el compilador de C++ crea en ejecución el constructor.
No obstante hubiera sido correcto haber definido un constructor que se encargara de, por
ejemplo, inicializar con 0 los datos miembros.
Un constructor es invocado automáticamente cuando se crea la instancia, o sea que no
hay llamarlo explícitamente desde el programa principal.
Existen 3 tipos de constructores:

- Constructor por defecto.


- Constructor común.
- Constructor de copia.

El constructor por defecto es, en caso que no lo haya definido, el que C++ en tiempo de
ejecución le asigne, o bien:

class Coordenadas
{
int x,y,z;

public:
Coordenadas(); //Constructor por defecto

};

También le podríamos haber agregado a este constructor, encerrados entre llaves, los
valores iniciales para los datos:
{x=0;y=0;z=0;}.
Cuando se crea el objeto se escribe:

void main(void)
{
Coordenadas coo;
....
}

El constructor común es aquel que recibe parámetros para asignarles como valores
iniciales a los datos miembros, o sea que al crear la instancia, se pasó unos parámetros
para inicializar.

class Coordenadas
{
int x,y,z;

public:
Coordenadas(int p, int q, int t) {x=p; y=q; z=t;} //Constructor común.

};

Cuando se crea el objeto se escribe:

void main(void)
{
Coordenadas coo(6,7,22); //Se le pasa los valores para inicializar.
.....
}

El constructor de copia se utilizan para inicializar un objeto con otro objeto de la misma
clase.

class Coordenadas
{
int x,y,z;
public:
Coordenadas ( int p, int q, int t) {x=p; y=q; z=t;} //Constructor común.
Coordenadas(const Coordenadas c) //Constructor de copia.
{
x=c.x;
y=c.y;
z=c.z;
}

};

Cuando se crea el objeto se escribe:

void main(void)
{
Coordenadas k(1,2,3); //Creación de un objeto con lo valores iniciales 1, 2 y
3.
Coordenadas coo=k; //Se llama al constructor de copia para que le asigne a coo
los valores de k.
....
}

Sobrecarga de funciones, (polimorfismo):

Habrá advertido en el último ejemplo de la clase, donde se ve el constructor de copia, que


también se define un constructor común. Bueno, eso es posible, una clase puede tener
varios constructores, que se irán usando de acuerdo a como uno cree el objeto,
(pasándole o no parámetros).
Pero, observe nuevamente, esta vez más detalladamente, la clase..., ¿no encuentra otra
cosa extraña?.
...(suspenso). : )

Los constructores son funciones, ¿¿¿cómo permite el compilador dos funciones con el
mismo nombre???.
Ahh, buena pregunta.

El compilador de C++ permitiría 100 funciones con el mismo nombre, el único requisito es
que cada una de ellas tenga diferente número y/o tipo de parámetros.
Esta cualidad, que no se aplica solamente a los constructores y funciones miembros de
una clase, sino que a cualquier función de un programa de C++, se llama Sobrecarga de
funciones o Polimorfismo.
Cuando se llama a la función, C++ selecciona de todas las funciones sobrecargadas
aquella que se ajusta de acuerdo con los parámetros pasados, en cantidad y tipo.

Funciones InLine:

También se puede estar preguntando, si las funciones miembros de una clase pueden
estar definidas fuera de la clase.
La respuesta es sí, por lo general las funciones miembros están definidas fuera de la
clase, dentro de esta última sólo se declararían los prototipos.
En el caso que la función esté definida dentro de la clase, ésta se llama función inline,
como las funciones Cargar() y Mostrar() de nuestra clase Coordenadas. Se podría incluso
agregar la cláusula inline, pero no hace falta.
¿Qué diferencia hay entre una función inline y otra, (definida dentro o fuera de la clase)?
Se define una función inline cuando es muy corta y simple, como los constructores y esas
funciones del ejemplo. Declarar una función en línea significa que el compilador puede, si
así lo decide, reemplazar cada invocación por la función, con la frecuencia que sea, por el
código encerrado entre llaves.
Hay que tener en cuenta que funciones inline extensas consumen más memoria, a pesar
que elimina el tiempo que lleva hacer la invocación.
Cuando se escribe una función fuera de la clase se especifica el acceso de la siguiente
forma:

NombreClase::Función() //Note que se accede con ::

Así quedaría nuestro programa, con la clase con un constructor por defecto y con las
funciones miembro fuera de la clase.

#include <iostream.h>

class Coordenadas
{
int x,y,z;
public:
Coordenadas(){x=0;y=0;z=0;} //Constructor por defecto.
void Cargar(void); //Prototipo de las funciones.
void Mostrar(void);
};

void Coordenadas::Cargar(void) //Definición de las funciones fuera de la clase


{
x=8;
y=9;
z=10;
}

void Coordenadas::Mostrar (void)


{
cout << x <<endl;
cout << y <<endl;
cout << z <<endl;
}

void main(void)
{
Coordenadas coo;

coo.Cargar();
coo.Mostrar();
}
Destructores:

Existe una función especial más para las clases, y se trata de los destructores.
Un destructor es una función miembro que se llama cuando se destruye la clase.
Todas las clases tiene un destructor implícito, incluso aunque no esté declarado. El
destructor implícito no hace nada en particular, pero si uno quiere, puede declarar un
destructor de forma explícita. Su sintaxis sería:

class NombreClase
{
...
public:
~NombreClase();
...
}

El destructor debe comenzar con el caracter "ñuflo", (~), seguido por el nombre de la
clase, (igual que el constructor). Además el destructor no puede recibir parámetros ni
retornar nada, (ni siquiera void).
No puede haber más de un destructor para una clase y si no se define uno
explícitamente, el compilador crea uno automáticamente.
El destructor se llama automáticamente siempre que una variable de ese tipo de clase,
(una instancia u objeto), sale fuera de su ámbito, (por ejemplo cuando termina el
programa).

Especificadores de acceso:

Ya había dicho que por defecto los datos miembros de una clase son privados. ¿Qué
significa esto?.
Que sólo las funciones miembros públicas de la misma clase tienen acceso a ellos. Si lo
desea puede escribir la cláusula private al momento de declarar los datos.
En cambio la cláusula public es obligatoria cuando se desea declarar un dato público y
este dato estará disponible para cualquier función del programa.
Existe una cláusula más, protected. Los datos definidos a continuación de esta cláusula
están restringidos para cualquier función externa a la clase, pero son públicos para la
propia clase y los miembros de clases derivadas.

Creación y Destrucción de Objetos

Ya se ha dicho que una clase es únicamente una especificación. Para poder utilizar la
funcionalidad contenida en la misma, se deben instanciar las clases.

1. Creación por Declaración.

Un objeto se puede instanciar de una forma simple, declarando una variable del tipo
de la clase.

En Ppal.h:

#include "ObjGraf.h"
En Ppal.cpp:

Pulsando dos veces en OnCreate de la pestaña Events del editor de objetos de


PpalFrm:

//--------------------------------------------------

void __fastcall TPpalFrm::FormCreate(TObject *Sender)


{
TObjGraf ObjGraf1();
TObjGraf ObjGraf2;
}

//--------------------------------------------------
Aunque esta forma es posible, y bastante utilizada en la programación de C++ clásica,
en C++ Builder se utiliza en muy contadas ocasiones. Esto es así por dos razones,
fundamentalmente:

La duración de los objetos suele ir más allá de una simple función o bloque. Debido al
enfoque de la programación dirigida por eventos, suele ser habitual que un objeto se
cree en un gestor de eventos y se destruya en otro.

No se puede usar esta modalidad de creación con la VCL.

Por lo tanto, nosotros no la utilizaremos.

Creación Dinámica

Es la forma habitual de crear objetos en C++ Builder, y se realiza mediante el


operador new .

Cuando usamos new para instanciar un objeto, se usa una variable que referencie o
apunte al nuevo objeto creado (de otra manera éste quedaría totalmente
inaccesible). En definitiva, se requiere la declaración previa de un puntero a objetos
del tipo del que se va a crear.

En Ppal.cpp:

TObjGraf * ObjGraf; // Variable Global.


// ObjGraf es un puntero a objetos de tipo TObjGraf

//--------------------------------------------------

void __fastcall TPpalFrm::FormCreate(TObject *Sender)


{
ObjGraf = new TObjGraf;
}

//--------------------------------------------------
La forma de establecer el estado inicial o destruir las componentes de un objeto se
estudiarán en el apartado dedicado a Constructores y Destructores (sección 5.4).

¡Cuidado! Cuando se utiliza esta forma de instanciación de clases es


responsabilidad únicamente del programador la correcta destrucción de los mismos.

Destrucción de objetos

Cuando un objeto deja de ser útil hay que eliminarlo. De esta manera la aplicación
recupera los recursos (memoria) que ese objeto había acaparado cuando se creó.

La destrucción de objetos creados en tiempo de ejecución con new se realiza


mediante el operador delete.

En Ppal.cpp:

Pulsando dos veces en OnDestroy de la pestaña Events del editor de objetos de


PpalFrm:

//--------------------------------------------------

void __fastcall TPpalFrm::FormDestroy(TObject *Sender)


{
delete ObjGraf;
}

Destructores en clases derivadas. Cuando remueve de la memoria un objeto de una clase


derivada, el recolector de basura invoca al destructor del objeto. Esto inicia una cadena
de invocaciones a destructores, en donde el destructor de la clase derivada y los
destructores de las clases bases directas e indirectas se ejecutan en orden inverso al que
se ejecutaron los constructores, esto es, primero se ejecuta el destructor de la clase
derivada y al final se ejecuta el destructor de la clase base ubicada en el nivel superior de
la jerarquía. La ejecución de los destructores debe liberar todos los recursos que el objeto
adquirió, antes de que el recolector de basura reclame la memoria de ese objeto. Cuando
el recolector de basura invoca al destructor de un objeto de una clase derivada, ese
destructor realiza su tarea y después invoca al destructor de la clase base. El proceso se
repite hasta que se invoca al destructor de la clase Object.

Ejemplo: // Destruct Derivadas?.cs : Destructores en clases derivadas. using C =


System.Console; class Animal { ~ Animal( ) { C.Write Line?(“Muere mi parte Animal
…”); } } class Mamífero : Animal { ~ Mamífero( ) { C.Write Line(“Muere mi parte Mamífero
…”); } } class Perro : Mamífero { ~ Perro( ) { C.Write Line(“Muere mi parte Perro …”); } }
public class Principal { static void Main( ) { Perro Fido = new Perro ( ); } }

Salida: Muere mi parte Perro … Muere mi parte Mamífero … Muere mi parte Animal …
El destructor es muy similar al constructor, excepto que es llamado automáticamente
cuando cada objeto sale de su ámbito de validez. Recordemos que las variables
automáticas tienen un tiempo de vida limitado, ya que dejan de existir cuando se sale del
bloque en que han sido declaradas. Cuando un objeto es liberado automáticamente, su
destructor, si existe, es llamado automáticamente.
Un destructor tiene el mismo nombre que la clase a la que pertenece, pero precedido con
una tilde (~). Un destructor no tiene tipo devuelto.
En el ejemplo anterior, podemos definir un destructor que asigne cero a las variables
antes de que sean liberadas, con lo que en realidad no estamos haciendo nada:

# include <iostream.h>

class Caja {
double longitud, anchura, altura;
public:
Caja (double dim1, double dim2, double dim3);
~Caja (void);
double volumen (void);
};

Caja :: Caja (double dim1, double dim2, double dim3)


{
longitud = dim1;
anchura = dim2;
altura = dim3;
};

Caja::~Caja (void) { longitud = 0; anchura = 0; altura = 0; } double Caja:: volumen


(void) {
return longitud * anchura * altura;
};

main ()
{
Caja pequeña(5, 4, 10), mediana (10, 6, 20), grande(20, 10, 30);

cout << "El volumen de la caja grande es " << grande.volumen() << '\n';
}

Si algún bloque de memoria fuera reservada dinámicamente en un objeto, se puede


utilizar el destructor para liberarla antes de que se pierdan los punteros a esas variables.

#include <iostream.h>

class Taco {
public:
Taco (int hard) {
hardness = new int;
*hardness = hard;
}
~Taco() {
cout << "Destroying taco with hardness " ;
cout << *hardness <<;\n';
delete hardness;
}
private:
int *hardness; };

main ()
{
Taco hard(10);
Taco *soft = new Taco (0);

delete soft;
};

En este ejemplo, vemos que el constructor tiene el mismo nombre que la clase, con un ~
delante. Cuando se crean punteros a clases, como soft en el ejemplo, se llama al
destructor cuando se libera la memoria del puntero. Si esto no se hace, nunca se llamará
al destructor.
Con clases declaradas estáticamente, como Taco hard, el destructor se llama al final de la
función donde se declara el objeto (en el ejemplo, al final de la función main.
Incluse cuando se interrumpe un programa usando una llamada a exit(), se llama a los
destructores de los objetos que existen en ese momento.

15. MEDIANTE 5 EJEMPLOS DE PROGRAMACION SEÑALE Y EXPLIQUE EL O LOS


DESTRUCTOR UTILIZADOS

Destructores
void __destruct ( void )

PHP 5 introduce un concepto de destructor similar a aquellos de otros lenguajes de


programación orientada a objetos, tal como C++. El método destructor será llamado tan
pronto como todas las referencias a un objeto en particular sean removidas o cuando el
objeto sea explícitamente destruido.

Ejemplo de Destructor
<?php
class MyDestructableClass {
function __construct() {
print "In constructor\n";
$this->name = "MyDestructableClass";
}

function __destruct() {
print "Destroying " . $this->name . "\n";
}
}

$obj = new MyDestructableClass();


?>

Como los constructores, los destructores de la clase padre no serán llamados


explícitamente por el compilador. Para ejecutar un destructor padre, se debe tener una
llamada explícita a parent::__destruct() en el cuerpo del destructor.

Nota: El destructor es llamado durante la finalización del script, de tal manera que los
headers ya han sido enviados.

Nota: Intentar arrojar una excepción desde un destructor produce un error fatal.

Ejemplo
En el siguiente ejemplo se crean tres clases que forman una cadena de herencia. La
clase First es la clase base, Second se deriva de First y Third se deriva de Second. Las
tres tienen destructores. En Main(), se crea una instancia de la clase más derivada.
Cuando ejecute el programa, observe que se llama a los destructores de las tres clases
automáticamente y en orden, desde la más derivada hasta la menos derivada.
class First

~First()

System.Console.WriteLine("First's destructor is called");

}
class Second: First

~Second()

System.Console.WriteLine("Second's destructor is called");

class Third: Second

~Third()

System.Console.WriteLine("Third's destructor is called");

class TestDestructors

static void Main()

Third t = new Third();

Resultados
Third's destructor is called

Second's destructor is called

First's destructor is called


class Second: First
{
~Second()
{
System.Console.WriteLine("Second's destructor is called");
}
}

class Third: Second


{
~Third()
{
System.Console.WriteLine("Third's destructor is called");
}
}

class TestDestructors
{
static void Main()
{
Third t = new Third();
}
}

Resultados
Third's destructor is called

Second's destructor is called

First's destructor is called

La destrucción del objeto’ a.) La destrucción de los objetos

Cuando un objeto no va a ser utilizado, el espacio de memoria de dinámica que utiliza ha


de ser liberado, así como los recursos que poseía, permitiendo al programa disponer de
todos los recursos posibles. A esta acción se la da el nombre de destrucción del objeto.
En Java la destrucción se puede realizar de forma automática o de forma personalizada,
en función de las características del objeto.

b.) La destrucción por defecto: Recogida de basura

El intérprete de Java posee un sistema de recogida de basura, que por lo general permite
que no nos preocupemos de liberar la memoria asignada explícitamente.

El recolector de basura será el encargado de liberar una zona de memoria dinámica que
había sido reservada mediante el operador new, cuando el objeto ya no va a ser utilizado
más durante el programa (por ejemplo, sale del ámbito de utilización, o no es referenciado
nuevamente).

El sistema de recogida de basura se ejecuta periódicamente, buscando objetos que ya no


estén referenciados.

c.) La destrucción personalizada: finalize

A veces una clase mantiene un recurso que no es de Java como un descriptor de archivo
o un tipo de letra del sistema de ventanas. En este caso sería acertado el utilizar la
finalización explícita, para asegurar que dicho recurso se libera. Esto se hace mediante la
destrucción personalizada, un sistema similar a los destructores de C++.

Para especificar una destrucción personalizada se añade un método a la clase con el


nombre finalize:

class Clase Finalizada?{


Clase Finalizada() { // Constructor
// Reserva del recurso no Java o recurso compartido
}
protected void finalize() {
// Liberación del recurso no Java o recurso compartido
}
}

El intérprete de Java llama al método finalize(), si existe cuando vaya a reclamar el


espacio de ese objeto, mediante la recogida de basura.

Debe observarse que el método finalize () es de tipo protected void y por lo tanto deberá
de sobreescribirse con este mismo tipo.
Un destructor es un método que pertenece a una clase y el mismo (en C++) debe tener el
mismo nombre de la clase a la que pertenece. A diferencia de los otros métodos de la
clase, un destructor deberá ser del tipo void, es decir, el mismo no regresará valor
alguno. Para diferenciar a un método destructor de un método constructor, al nombre del
destructor se le debe anteponer el caracter ~ (Alt + 126).

El objetivo principal del destructor es el de retirar de la memoria al objeto, o sea, el


destructor hace todo lo contrario que el constructor.

Los destructores suelen usarse para liberar memoria que haya sido solicitada por el
objeto a travez de las ordenes malloc(), new, etc. En tales casos se deberá incluir dentro
del método destructor la orden free, delete, etc., según sea el caso.

// clase Pareja con constructor y destructor


class Pareja
{
// atributos
double a, b;

public:
// constructor de base ( nulo )
Pareja() {}

// constructror parametrizado
Pareja(double x, double y) : a(x), b(y) {}

// destructor
~Pareja() {}

// métodos
double getA();
double getB();
void setA(double n);
void setB(double n);
};

Você também pode gostar