PROGRAMACIÓN EN JAVA

Francisco Javier Cruz Vílchez

Capitulo 4
CLASES Y METODOS

 

Objetivos:
Al finalizar este capítulo, el alumno aprenderá a:

  • Definir una clase y crear una instancia de la clase
  • Manejara los modificadores de acceso de las clases y de sus métodos.
  • Aprender a definir un constructor de clase y sobre carga de estos
  • Conocer  sobrecarga de métodos.

4.1 Declaración de clase.


La declaración mínima para una clase es la siguiente:
class NombreClase
Una declaración de este tipo indica que la clase no desciende de ninguna otra, aunque en realidad, todas las clases declaradas en un programa escrito en Java son descendientes, directa o indirectamente, de la clase Object que es la raíz de toda la jerarquía de clases en Java.

class ObjetoSimpleCreado {
String variable = "Una variable";
int entero = 14;
public String obtnerString() {
return variable;
    }
}
class ObjetoSimple {
public static void main(String arumentos[]) {
ObjetoSimpleCreado varObj = new ObjetoSimpleCreado();
System.out.println(varObj.toString());
}
}

Muestra en pantalla la siguiente línea de texto:
ObjetoSimpleCreado@13937d8

En este caso, la clase ObjetoSimpleCreado ha sido declarada como no descendiente de ninguna otra clase, pero a pesar de ello, hereda de la superclase
Object (java.lang.Object) todos sus métodos, entre los que se encuentran el método toString() que, en este caso, devuelve el siguiente valor: “ObjetoSimpleCreado@13937d8” (el nombre de la clase junto con el puntero al objeto). Este método, que heredan todas las clases que puedan declararse, debería ser redefinido por el programador para mostrar un valor más significativo. Si en lugar de la instrucción System.out.println(varObj.toString()); se hubiera utilizado la siguiente: System.out.println(varObj.obtenerString()) la salida por pantalla habría sido:
Una variable


4.2 Modificadores de clase.


Los modificadores de clase son palabras reservadas que se anteponen a la declaración de clase. Los modificadores posibles son los siguientes:

La sintaxis general es la siguiente:
modificador class NombreClase [ extends NombreSuperclase] [implements listaDeInterfaces]

Si no se especifica ningún modificador de clase, la clase será visible en todas las declaradas en el mismo paquete16. Si no se especifica ningún paquete, se considera que la clase pertenece a un paquete por defecto al cual pertenecen todas las clases que no declaran explícitamente el paquete al que pertenecen.

Si no se especifica ningún modificador de clase, la clase será visible en todas las declaradas en el mismo paquete Si no se especifica ningún paquete, se considera que la clase pertenece a un paquete por defecto al cual pertenecen todas las clases que no declaran explícitamente el paquete al que pertenecen.
4.2.1 public.
Cuando se crean varias clases que se agrupan formando un paquete (package), únicamente las clases declaradas public pueden ser accedidas desde otro  paquete.
Toda clase public debe ser declarada en un fichero fuente con el nombre de esa clase pública: NombreClase.java. De esta afirmación se deduce que en un fichero fuente puede haber más de una clase, pero sólo una con el modificador public.
4.2.2 abstract.
Las clases abstractas no pueden ser instanciadas sirven únicamente para declarar subclases que deben redefinir aquellos métodos que han sido declarados abstract. Esto no quiere decir que todos los métodos de una clase abstracta deban ser abstractos, incluso es posible que ninguno de ellos lo sea. Aún en este último caso, la clase será considerada como abstracta y no podrán declararse objetos de esta clase.
Cuando alguno de los métodos de una clase es declarado abstracto, la clase debe ser obligatoriamente abstracta, de lo contrario, el compilador genera un mensaje de error. Todas estas clases se crean en el paquete por defecto.

abstract class Animal {
String nombre;
int patas;
public Animal(String n, int p) {
nombre=n;
patas=p;
}
    abstract void habla(){};
               // método abstracto que debe ser redefinido por las subclases
}

class Perro extends Animal {
// La clase perro es una subclase de la clase abstracta Animal
String raza;
public Perro(String n, int p, String r) {
super(n,p);
raza=r;
      }
public void habla() {
// Este método es necesario redefinirlo para poder instanciar
// objetos de la clase Perro
System.out.println("Me llamo "+nombre+": GUAU, GUAU");
System.out.println("mi raza es "+raza);
    }
}

class Gallo extends Animal {
// La clase Gallo es una subclase de la clase abstracta Animal
public Gallo(String n, int p) {
       super(n,p);
}
public void habla() {
// Este método es necesario redefinirlo para poder instanciar
// objetos de la clase Gallo
        System.out.println("Soy un Gallo, Me llamo "+nombre);
        System.out.println("Kikirikiiii");
}
}
class Abstracta {
public static void main(String argumentos[]) {
Perro toby = new Perro("Toby",4,"San Bernardo");
Gallo kiko = new Gallo("Kiko",2);
kiko.habla();
System.out.println();
toby.habla();
}
}
Salida por pantalla del programa:
Soy un Gallo, Me llamo Kiko
Kikirikiiii
Me llamo Toby: GUAU, GUAU
mi raza es San Bernardo

El intento de declarar un objeto del tipo Animal, que es abstract, habría generado un mensaje de error por el compilador. Las clases abstractas se crean para ser superclases de otras clases. En este ejemplo, se ha declarado el método habla() como abstracto porque queremos que todos los animales puedan hablar, pero no sabemos qué es lo que van a decir (qué acciones se van a realizar), por lo que es declarada de tipo abstract. Las clases que heredan de Animal deben implementar un método habla() para poder heredar las características de Animal.

4.2.3 final.
Una clase declarada final impide que pueda ser superclase de otras clases. Dicho de otra forma, ninguna clase puede heredar de una clase final. Esto es importante cuando se crean clases que acceden a recursos del sistema operativo o realizan operaciones de seguridad en el sistema. Si estas clases no se declaran como final, cualquiera podría redefinirlas y aprovecharse para realizar operaciones sólo permitidas a dichas clases pero con nuevas intenciones, posiblemente oscuras.
A diferencia del modificador abstract, pueden existir en la clase métodos final sin que la clase que los contiene sea final (sólo se protegen algunos métodos de la clase que no pueden ser redefinidos). Una clase no puede ser a la vez abstract y final ya que no tiene sentido, pero sí que puede ser public abstract o public final.

4.3 El cuerpo de la clase.


Una vez declarada la clase, se declaran los atributos y los métodos de la misma dentro del cuerpo.
Declaración de clase
 {
Declaración de atributos
Declaración de clases interiores
Declaración de Métodos
}
La declaración de clases interiores (también conocidas como clases anidadas) no es imprescindible para programar en Java..

4.3.1 Declaración de atributos.
Los atributos sirven, en principio, para almacenar valores de los objetos que se
instancian a partir de una clase.

La sintaxis general es la siguiente:
[modificadorDeÁmbito] [static] [final] [transient] [volatile] tipo
nombreAtributo

Existen dos tipos generales de atributos:
· Atributos de objeto.
· Atributos de clase.
class Calculadora {
static public int factorial(int n) {
int fact=1;
while (n>0) {
fact *=n--;
}
return fact;
}
}
public class app {
     public static void main(String[] args) {
System.out.println(Calculadora.factorial(5));
   }
}

En el ejemplo calculadora no ha hecho falta crear un objeto para calcular el factorial esto se puede realizar por que el método factorial de la clase calculadora se le ha indicado con que es stactic, caso contrario no hubiéramos podido utilizar la el método de la clase sin necesidad de de definir el un objeto previamente.
Los atributos de clase son variables u objetos que almacenan el mismo valor para todos los objetos instanciados a partir de esa clase, esto lo podríamos utilizar para el caso que necesitáramos contar cuantos objetos tenemos de una determinad clase.
Dicho de otra forma: mientras que a partir de un atributo de objeto se crean tantas copias de ese atributo como objetos se instancien, a partir de un atributo de clase sólo se crea una copia de ese atributo que será compartido por todos los objetos que se instancien. Si no se especifica lo contrario, los atributos son de objeto y no de clase. Para declarar un atributo de clase se utiliza la palabra reservada static.
La declaración mínima de los atributos es:

tipo nombreAtributo

Si existen varios atributos del mismo tipo (en la misma clase), se separan sus nombres mediante comas ( , ):
class Punto {
int x, y;
String nombre;

}

4.3.1.1 Atributos static.
Mediante la palabra reservada static se declaran atributos de clase.

class Persona {
static int numPersonas=0; // atributo de clase
String nombre; // atributo de objeto
public Persona (String n) {
nombre = n;
numPersonas++;
}
public void muestra() {
System.out.print("Soy "+nombre);
System.out.println(" pero hay "+ (numPersonas-1) +" personas más.");
   }
}

class Static {
public static void main(String argumentos[]) {
Persona p1,p2,p3;
// se crean tres instancias del atributo nombre
// sólo se crea una instancia del atributo numPersonas
p1 = new Persona("Pedro");
p2 = new Persona("Juan");
p3 = new Persona("Susana");
p2.muestra();

p1.muestra();
    }
}
Salida por pantalla:

Soy Juan pero hay 2 personas más.
Soy Pedro pero hay 2 personas más.

En este caso, numPersonas es un atributo de clase y por lo tanto es compartido por todos los objetos que se crean a partir de la clase Persona. Todos los objetos de esta clase pueden acceder al mismo atributo y manipularlo. El atributo nombre es un atributo de objeto y se crean tantas instancias como objetos se declaren del tipo Persona. Cada variable declarada de tipo Persona tiene un atributo nombre y cada objeto puede manipular su propio atributo de objeto. En el ejemplo, se crea un atributo numPersonas y tres atributos nombre (tantos
como objetos de tipo Persona).

4.3.1.3 Atributos final.
La palabra reservada final calificando a un atributo o variable sirve para declarar constantes, no se permite la modificación de su valor. Si además es static, se puede acceder a dicha constante simplemente anteponiendo el nombre de la clase, sin necesidad de instanciarla creando un objeto de la misma. El valor de un atributo final debe ser asignado en la declaración del mismo. Cualquier intento de modificar su valor generará el consiguiente error por parte del compilador.
class Circulo {
final double PI=3.14159265;
int radio;
Circulo(int r) {
radio=r;
}
public double area() {
return PI*radio*radio;
}
}
class Final {
public static void main(String argumentos[]) {
Circulo c = new Circulo(15);
System.out.println(c.area());
}
}

Podría ser útil en algunos casos declarar una clase con constantes:
class Constantes {
static final double PI=3.14159265;
static final String NOMBREEMPRESA = "Ficticia S.A.";
static final int MAXP = 3456;
static final byte CODIGO = 1;
}

Para acceder a estas constantes, no es necesario instanciar la clase Constantes, ya que los atributos se han declarado static. Simplemente hay que anteponer el nombre de la clase: Constantes.PI, Constantes.CODIGO, etc. Para utilizarlas

4.3.2 Modificadores de ámbito de atributos.
Los modificadores de ámbito de atributo especifican la forma en que puede accederse a los mismos desde otras clases. Estos modificadores de ámbito son:

· El ámbito por defecto.

4.3.2.1 Atributos private.
El modificador de ámbito private es el más restrictivo de todos. Todo atributo private es visible únicamente dentro de la clase en la que se declara. No existe ninguna forma de acceder al mismo si no es a través de algún método (no private) que devuelva o modifique su valor.

Una buena metodología de diseño de clases es declarar los atributos private siempre que sea posible, ya que esto evita que algún objeto pueda modificar su valor si no es a través de alguno de sus métodos diseñados para ello.

4.3.2.2 Atributos public.

El modificador de ámbito public es el menos restrictivo de todos. Un atributo public será visible en cualquier clase que desee acceder a él, simplemente anteponiendo el nombre de la clase.  Las aplicaciones bien diseñadas minimizan el uso de los atributos public y maximizan el uso de atributos private. La forma apropiada de acceder y modificar atributos de objetos es a través de métodos que accedan a los mismos, aunque en ocasiones, para acelerar el proceso de programación, se declaran de tipo public y se modifican sus valores desde otras clases.

final class Empleado {
public String nombre;
public String dirección;
private int sueldo;
}
En este ejemplo existen dos atributos public (nombre y dirección) y uno private (sueldo). Los atributos nombre y dirección podrán ser modificados por cualquier clase, por ejemplo de la siguiente forma:

emple1.nombre=”Pedro López”;

Mientras que el atributo sueldo no puede ser modificado directamente por  ninguna clase que no sea Empleado. En realidad, para que la clase estuviera bien diseñada, se deberían haber declarado private los tres atributos y declarar métodos para modificar los atributos. De estos métodos, el que modifica el atributo sueldo podría declararse de tipo private para que no pudiera ser utilizado por otra clase distinta de Empleado.

 

4.3.2.3 Atributos protected.
Los atributos protected pueden ser accedidos por las clases del mismo paquete (package) y por las subclases del mismo paquete, pero no pueden ser accedidas por subclases de otro paquete, aunque sí pueden ser accedidas las variables protected heredadas de la primera clase.
Esto parece un poco confuso, veámoslo con un ejemplo:

package PProtegido;
public class Protegida {
protected int valorProtegido;
public Protegida(int v) {
valorProtegido=v;
}
}

package PProtegido;
public class Protegida2 {
public static void main(String argumentos[]) {
Protegida p1= new Protegida(0);
p1.valorProtegido = 4;
System.out.println(p1.valorProtegido);
      }
}
En este caso, por pertenecer la clase Protegida2 al mismo paquete que la clase Protegida, se puede acceder a sus atributos protected. En nuestro caso, valorProtegido.

package OtroPaquete;
import PProtegido.*;
public class Protegida3 {
public static void main(String argumentos[]) {
Protegida p1= new Protegida(0);
p1.valorProtegido = 4;
System.out.println(p1.valorProtegido);
 }
}
En este caso, se importa el paquete Pprotegido para poder acceder a la clase Protegida, pero el paquete en el que se declara la clase Protegida3 es OtroPaquete distinto al que contiene Protegida, por lo que no se puede acceder a sus atributos protected. El compilador Java mostraría un error al acceder a valorProtegido en la línea p1.valorProtegido = 4; (puesto que intenta acceder, para modificar, un atributo protegido) y en la línea System.out.println(p1.valorProtegido); (por el mismo motivo, a pesar de que se trate sólo de leer el valor).

Sin embargo:
package OtroPaquete;
import PProtegido.*;
public class Protegida4 extends Protegida {
public Protegida4(int v) {
super(v);
}
public void modifica(int v) {
valorProtegido=v;
}          
}
En este caso, se ha declarado una subclase de Protegida. Esta clase puede  acceder a sus atributos (incluso a valorProtegido), por ejemplo, a través del método modifica, pero:

package OtroPaquete;
public class EjecutaProtegida4 {
public static void main(String argumentos[]) {
Protegida4 p1= new Protegida4(0);
p1.valorProtegido = 4;
System.out.println(p1.valorProtegido);
}
}

En este caso, a pesar de que (el objeto) la variable es del tipo Protegida4 (subclase de Protegida), y la clase EjecutaProtegida4 pertenece al mismo paquete que Protegida4, no se puede acceder a los atributos private. Sólo los métodos de la clase Protegida4 pueden hacerlo. Así:

package OtroPaquete;
public class EjecutaProtegida4_2 {
public static void main(String argumentos[]) {
Protegida4 p1= new Protegida4(0);
p1.modifica(4);
}
}
Esta clase sí que puede modificar el atributo protected pero únicamente a través del método de la clase Ejecuta4 denominado modifica.

En resumen: Un atributo protegido sólo puede ser modificado por clases del mismo paquete, ahora bien, si se declara una subclase entonces esa subclase es la encargada de proporcionar los medios para acceder al atributo protegido.

4.3.2.4 El ámbito por defecto de los atributos.
Los atributos que no llevan ningún modificador de ámbito pueden ser accedidos desde las clases del mismo paquete, pero no desde otros paquetes.

 

4.3.2.5 Resumen de ámbitos de atributos.

 

Acceso desde

Modificador

Misma Clase

SubClase

MismoPaquete

TodoelMundo

Private

Si

No

No

No

Public

Si

Si

Si

Si

Protected

Si

Según

Si

No

Por defecto

Si

No

Si

No

4.4 Métodos


Sintaxis general de los métodos:
Declaración de método {
Cuerpo del método
}

4.4.1 Declaración de método.
La declaración mínima sin modificadores de un método es:

TipoDevuelto NombreMétodo (ListaParámetros)

Donde:

Cuando se declara una subclase, esa subclase hereda, en principio, todos los atributos y métodos de la superclase (clase padre). Estos métodos pueden ser redefinidos en la clase hija simplemente declarando métodos con los mismos identificadores, parámetros y tipo devuelto que los de la superclase. Si desde uno de estos métodos redefinidos se desea realizar una llamada al método de la superclase, se utiliza el identificador de la superclase y se le pasan los parámetros.

Ejemplo: suponiendo que se ha declarado una clase como heredera de otra (SuperC) en la que existe el método int mayor(int x, int y), se puede redefinir este método simplemente declarando un método con el mismo identificativo y parámetros int mayor(int x , int y). Si desde dentro del método redefinido se desea hacer referencia al método original se podría utilizar: var = SuperC(x,y); También se pueden declarar métodos para una misma clase con los mismos identificadores pero con parámetros distintos.
class Mayor {
// Declara dos métodos con el mismo identificativo
// uno de ellos acepta dos enteros y el otro dos
// enteros largos.
// ambos métodos devuelven el valor mayor de los
// dos enteros que se pasan como parámetros.
static int mayor(int x, int y) {
if (x>y)
return x;
else
return y;
}
static long mayor(long x, long y) {
if (x>y)
return x;
else
return y;
}
}
class ConstantesMayor {
static final int INT = 15;
static final long LONG = 15;
}
class SubMayor extends Mayor {
// modifica la clase Mayor de la siguiente forma:
// los métodos devuelven el valor mayor de entre
// los dos parámetros que se le pasan, pero
// siempre, como mínimo devuelve el valor
// de las constantes INT y LONG

static int mayor(int x, int y) {
// llama al método mayor de la superclase
int m = Mayor.mayor(x,y);
return Mayor.mayor(m,ConstantesMayor.INT);
}

static long mayor(long x, long y) {
// llama al método mayor de la superclase
long m = Mayor.mayor(x,y);
return Mayor.mayor(m,ConstantesMayor.LONG);
}
}
class EjecutaMayor {
public static void main(String argumentos[]) {
int int1=12,int2=14;
long long1=20, long2=10;
System.out.println("ENTEROS:");
System.out.println("mayor de 12 y 14 = " +Mayor.mayor(int1,int2));
System.out.println("mayor de 12 y 14 y 15 ="+SubMayor.mayor(int1,int2));
System.out.println("ENTEROS LARGOS:");
System.out.println("mayor de 20 y 10 = " +Mayor.mayor(long1,long2));
System.out.println("mayor de 20 y 10 y 15 ="+SubMayor.mayor(long1,long2));
}
}

Declaración completa de métodos.

Sintaxis general:

[ModificadorDeÁmbito] [ static] [abstract] [ final] [ native] [synchronizedTipoDevuelto NombreMétodo ( [ ListaParámetros] ) [throws ListaExcepciones]

Devolución De Valores

Los métodos pueden devolver valores básicos (int, short, double, etc.), Strings, arrays e incluso objetos.
En todos los casos es el comando return el que realiza esta labor. En el caso de arrays y objetos, devuelve una referencia a ese array u objeto. Ejemplo:

class FabricaArrays {
public int[] obtenArray(){
int array[]= {1,2,3,4,5};
return array;
    }
}
public class returnArray {
public static void main(String[] args) {
FabricaArrays fab=new FabricaArrays();
int nuevoArray[]=fab.obtenArray();
}
}

SobreCarga de Métodos
Una propiedad de la POO es el polimorfismo. Java posee esa propiedad ya que admite sobrecargar los métodos. Esto significa crear distintas variantes del mismo método.
Ejemplo:
class Matemáticas{
public double suma(double x, double y) {
return x+y;
}
public double suma(double x, double y, double z){
return x+y+z;
}
public double suma(double[] array){
double total =0;
for(int i=0; i<array.length;i++){
total+=array[i];
}
return total;
}
La clase matemáticas posee tres versiones del método suma. una versión que suma dos números double, otra que suma tres y la última que suma todos los miembros de un array de doubles. Desde el código se puede utilizar cualquiera de las tres versiones según convenga.

4.4.1.1 Métodos static.
Los métodos static son métodos de clase (no de objeto) y por tanto, no necesita instanciarse la clase (crear un objeto de esa clase) para poder llamar a ese  método. Se ha estado utilizando hasta ahora siempre que se declaraba una clase ejecutable, ya que para poder ejecutar el método main() no se declara ningún objeto de esa clase.
Los métodos de clase (static) únicamente pueden acceder a sus atributos de clase (static) y nunca a los atributos de objeto (no static). Ejemplo:

 

class EnteroX {
int x;
static int x() {
return x;
}
static void setX(int nuevaX) {
x = nuevaX;
}
}

Mostraría el siguiente mensaje de error por parte del compilador:
MetodoStatic1.java:4: Can't make a static reference to
nonstatic variable x in class EnteroX.
return x;
^
MetodoStatic1.java:7: Can't make a static reference to
nonstatic variable x in class EnteroX.
x = nuevaX;
^
2

Sí que sería correcto:
class EnteroX {
static int x;
static int x() {
return x;
}
  static void setX(int nuevaX) {
x = nuevaX;
  }
}
Al ser los métodos static, puede accederse a ellos sin tener que crear un objeto EnteroX:

class AccedeMetodoStatic {
public static void main(String argumentos[]) {
EnteroX.setX(4);
System.out.println(EnteroX.x());
   }
}

4.4.1.2 Métodos abstract.

Los métodos abstract se declaran en las clases abstract. Es decir, si se declara algún método de tipo abstract, entonces, la clase debe declararse obligatoriamente como abstract.

Cuando se declara un método abstract, no se implementa el cuerpo del método, sólo su signatura. Las clases que se declaran como subclases de una clase abstract deben  implementar los métodos abstract. Una clase abstract no puede ser instanciada, únicamente sirve para ser utilizada como superclase de otras clases.

4.4.1.3 Métodos final.
Los métodos de una clase que se declaran de tipo final no pueden ser redefinidos por las subclases. Esta opción puede adoptarse por razones de seguridad, para que nuestras clases no puedan ser extendidas por otros.

abstract class Animal {
String nombre;
int patas;
public Animal(String n, int p) {
nombre=n;
patas=p;
}
public final int numPatas(){
return patas;
}
abstract void habla();
}

 

class Perro extends Animal {
String raza;
public Perro(String n, int p, String r) {
super(n,p);
raza=r;
  }
public void habla() {
System.out.println("Me llamo "+nombre+": GUAU, GUAU");
System.out.println("mi raza es "+raza);
System.out.println("Tengo "+numPatas()+" patas.");
  }
}

class Gallo extends Animal {
public Gallo(String n, int p) {
super(n,p);
}
public void habla() {
System.out.println("Soy un Gallo, Me llamo "+nombre);
System.out.println("Kikirikiiii");
}
}

class FinalAbstracta {
public static void main(String argumentos[]) {
Perro toby = new Perro("Toby",4,"San Bernardo");
Gallo kiko = new Gallo("Kiko",2);
kiko.habla();
System.out.println();
toby.habla();
}
}
En este caso, la clase Animal es abstracta (no puede instanciarse), sólo puede utilizarse como superclase de otras (Perro y Gallo). Uno de los métodos de Animal es final y por lo tanto no puede redefinirse. Cualquier intento de declarar un método (numPatas) en cualquier subclase de Animal generaría un error del compilador.

En el siguiente caso tenemos otro caso donde hemos definido una clase abstracta.

abstract class vehiculo {
public int velocidad=0;
abstract public void acelera();
public void para() {velocidad=0;}
}
class coche extends vehiculo {
public void acelera() {
   velocidad+=5;
}
}
public class prueba {
public static void main(String[] args) {
coche c1=new coche();
c1.acelera();
System.out.println(c1.velocidad);
c1.para();
System.out.println(c1.velocidad);
}
}

4.4.1.4 Modificadores de ámbito de los métodos.
Los modificadores de ámbito de los métodos son exactamente iguales que los de los atributos, especifican la forma en que puede accederse a los mismos desde otras clases. Estos modificadores de ámbito son:

 

 

Acceso desde

Modificador

Misma Clase

SubClase

MismoPaquete

TodoelMundo

Private

Si

No

No

No

Public

Si

Si

Si

Si

Protected

Si

Según

Si

No

Por defecto

Si

No

Si

No

4.5 Constructores.


Un constructor es un método especial de las clases que sirve para inicializar los objetos que se instancian como miembros de una clase.

Para declarar un constructor basta con declarar un método con el mismo nombre que la clase. No se declara el tipo devuelto por el constructor (ni siquiera void), aunque sí que se pueden utilizar los modificadores de ámbito de los métodos: public, protected, private.

Los constructores tienen el mismo nombre que la clase y todas las clases tienen uno por defecto (que no es necesario declarar), aunque es posible sobrescribirlo e incluso declarar distintos constructores (sobrecarga de métodos) al igual que los demás métodos de una clase.

class Nif {
int dni;
char letra;
static char tabla[]={'T','R','W','A','G','M','Y','F','P',
'D','X','B','N','J','Z','S','Q','V',
'H','L','C','K','E'};

public Nif(int ndni,char nletra) throws NifException{
if (Character.toUpperCase(nletra)==tabla[ndni%23]) {
dni=ndni;
letra=Character.toUpperCase(nletra);
}
else
    throw new LetraNifException("Letra de NIF incorrecta");
}

public Nif(int ndni) {
dni=ndni;
letra=tabla[dni%23];
}

public Nif(String sNif) throws NifException, LetraNifException {
char letraAux;
StringBuffer sNumeros= new StringBuffer();
int i,ndni;
for (i=0;i<sNif.length();i++) {
if ("1234567890".indexOf(sNif.charAt(i))!=-1) {
sNumeros.append(sNif.charAt(i));
     }
}
try {
      dni=Integer.parseInt(sNumeros.toString());
      letraAux=Character.toUpperCase(sNif.charAt(
      sNif.length()-1));
 } catch (Exception ex) {
       throw new NifException("NIF incorrecto");
  }
letra=tabla[dni%23];
     if ("ABCDEFGHIJKLMNOPQRSTUVWXYZ".indexOf(letraAux)!=-1) {
        if (letraAux!=letra) {
         throw new LetraNifException("Letra de NIF incorrecta");
        }
      } else letra=tabla[dni%23];
}

public char obtenerLetra() {
    return letra;
}
public int obtenerDni() {
    return dni;
}
public String toString() {
   return (String.valueOf(dni)+String.valueOf(letra));
}

public String toStringConFormato() {
  String sAux= String.valueOf(dni);
  StringBuffer s = new StringBuffer();
  int i;

for (i=sAux.length()-1;i>2;i-=3) {
    s.insert(0,sAux.substring(i-2,i+1));
    s.insert(0,".");
}

s.insert(0,sAux.substring(0,i+1));
s.append('-');
s.append(letra);
return (s.toString());
}

static char letraNif(int ndni) {
return tabla[ndni%23];
}
static char letraNif(String sDni) throws NifException {
Nif j = new Nif(sDni);
return j.obtenerLetra();
 }
}
class NifException extends Exception {
  public NifException() { super(); }
  public NifException(String s) { super(s); }
}

class LetraNifException extends NifException {
public LetraNifException() { super(); }
public LetraNifException(String s) { super(s); }
}

En el ejemplo anterior, la clase Nif tiene tres constructores:

public Nif(int ndni,char nletra) throws LetraNifException
public Nif(int ndni)
public Nif(String sNif) throws NifException, LetraNifException

Para inicializar un objeto de una determinada clase se llama a su constructor después de la palabra reservada new.

 

class EjecutaNif {
public static void main(String argumentos[]) {
Nif n;
int dni;
if (argumentos.length!=1) {
System.out.println("Uso: EjecutaNif dni");
return;
}
else {
dni = Integer.valueOf(argumentos[0]).intValue();
n = new Nif(dni);
System.out.println("Nif: "+n.toStringConFormato());
}
    }
}
En este ejemplo, se está llamando al segundo constructor, aquel que acepta como parámetro un entero.
En él se acepta como argumento en la línea de comandos un DNI y muestra el NIF correspondiente:
java EjecutaNif 18957690

mostraría la siguiente salida por pantalla:
Nif: 18.957.690-D

Es bastante habitual cuando se sobrescribe el constructor o constructores de la superclase el realizar una llamada al constructor de la superclase y después realizar otras operaciones de inicialización de la clase hija. Esto, como ya se ha visto en el punto anterior, se realiza utilizando el identificador de clase super. Así, si se declarara una subclase de Nif, y se sobrescribiera alguno de sus constructores, se podría realizar en primer lugar, una llamada al constructor de Nif.

 

class HijaNif extends Nif {
public static numNifs = 0;

public Nif(int ndni) {
super.Nif(ndni);
numNifs++;
}

}
En este caso se ha declarado una clase HijaNif que añade un atributo de clase27 que sirve de contador del número de objetos que se instancian de la clase HijaNif. Se rescribe el constructor (constructores) de forma que se incrementa este contador por cada objeto declarado.
Tenemos otro ejemplo donde definimos un constructor de la clase Ficha

class Ficha {
private int casilla;
Ficha() { //constructor
casilla = 1;
}
public void avanzar(int n) {
casilla += n;
}
public int casillaActual(){
return casilla;
}
}
public class app {
public static void main(String[] args) {
Ficha ficha1 = new Ficha();
ficha1.avanzar(3);
System.out.println(ficha1.casillaActual());//Da 4
        }
}

En la línea Ficha ficha1 = new Ficha(); es cuando se llama al constructor, que es el que
coloca inicialmente la casilla a 1. Pero el constructor puede tener parámetros como en el siguiente caso:

class Ficha {
private int casilla; //Valor inicial de la propiedad
Ficha(int n) { //constructor
casilla = n;
}
public void avanzar(int n) {
casilla += n;
}
public int casillaActual(){
return casilla;
}
}

public class app {
public static void main(String[] args) {
Ficha ficha1 = new Ficha(6);
ficha1.avanzar(3);
System.out.println(ficha1.casillaActual());//Da 9
    }
}

4.6 Destructores.

Un destructor es un método de la clase que sirve para realizar una serie de operaciones cuando un objeto perteneciente a la clase deja de existir. Operaciones típicas en los objetos cuando desaparecen son la liberación de recursos del sistema que tuviera asignados el objeto: liberación de memoria que pudiera tener reservada el objeto, cierre de los ficheros y sockets que tuviera abiertos, etc..

En Java existe un Thread del sistema “Garbage collector” literalmente: Recolector de Basura, que se ejecuta regularmente para liberar la memoria asignada a objetos que ya no se necesitan. A pesar de ello, puede ser necesario realizar algunas operaciones adicionales. Para ello hay que declarar un método de la siguiente forma:

protected void finalize() throws throwable

Por ejemplo, en la clase HijaNif, se utiliza un contador para saber el número de objetos instanciados de la clase HijaNif. Para decrementar numNifs, habría que declarar el método finalize():

protected void finalize() throws throwable {
numNifs--;
super.finalize();
}

Es conveniente llamar al método super.finalize(), el destructor de la superclase, para liberar recursos que pudiera tener asignados la clase heredados transparentemente de la clase padre y de los cuales no se tuviera conocimiento.


4.7 INTERFACES.

La limitación de que sólo se puede heredar de una clase, hace que haya problemas ya que muchas veces se deseará heredar de varias clases. Aunque ésta no es la finalidad directa de las interfaces, sí que tiene cierta relación.

Mediante interfaces se definen una serie de comportamientos de objeto. Estos comportamientos puede ser “implementados” en una determinada clase. No definen el tipo de objeto que es, sino lo que puede hacer (sus capacidades). Por ello lo normal es que el nombre de las interfaces terminen con el texto “able” (configurable, modificable, cargable).

Por ejemplo en el caso de la clase Coche, esta deriva de la superclase Vehículo, pero además puesto que es un vehículo a motor, puede implementar métodos de una interfaz llamada por ejemplo arrancable. Se dirá entonces que la clase Coche es arrancable. utilizar interfaces.

Para hacer que una clase utilice una interfaz, se añade detrás del nombre de la clase la palabra implements seguida del nombre del interfaz. Se pueden poner varios nombres de interfaces separados por comas (solucionando, en cierto modo, el problema de la herencia múltiple).

Definimos  la interfaz de  pila
public interface Stack {
      public int size();
      public boolean isEmpty();
      public void push(Object o);
      public Object pop() throws StackEmptyException;
      public Object top()  throws StackEmptyException;
   }

Tenemos una clase para el manejo de las excepciones.

public class StackEmptyException extends Exception{
      public StackEmptyException(){
         super();
      }
      public StackEmptyException(String mensaje){
         super(mensaje);
      }
   }

Realizamos la implementación de la pila utilizando la interfaz definida anteriormente utilizando listas.

public class LinkedStack implements Stack
   {
      private Node top;
      private int size;
  
      public int size() {
         return size;
      }
      public boolean isEmpty() {
         return (top==null);
      }
      public void push(Object e) {
         Node n=new Node();
         n.setElem(e);
         n.setNext(top);
         top=n;
         size++;
      }
      public Object top()
      throws StackEmptyException{
         if (isEmpty())
            throw new StackEmptyException("vacia");
         return top.getElem();
      }
  
      public Object pop()
      throws StackEmptyException{
         Object temp;
         if (isEmpty())
            throw new StackEmptyException("vacia");
         temp=top.getElem();
         top=top.getNext();
         size--;
         return temp;
      }
   }

Para el mismo caso de pilas realizamos la implementación de la interfaz utilizando la clase vector.

import java.util.Vector;
   public class VectorStack implements Stack{
      private Object top;
      private Vector pila;
  
      public VectorStack(){
         pila = new Vector();
      }
  
      public int size(){
         return pila.size();
      }
      public boolean isEmpty(){
         return (pila.size() == 0);
      }
      public void push(Object o){
         pila.addElement(o);
      }
      public Object pop() throws StackEmptyException{
         Object o;
     
         if (pila.size() == 0) throw new StackEmptyException();
         o = pila.remove(pila.size() - 1);
         return o;
      }
      public Object top() throws StackEmptyException{
         if (pila.size() == 0)
            throw new StackEmptyException();
         else
            return pila.lastElement();
      }
   }

 

Volver al índice

Enciclopedia Virtual
Tienda
Libros Recomendados


1647 - Investigaciones socioambientales, educativas y humanísticas para el medio rural
Por: Miguel Ángel Sámano Rentería y Ramón Rivera Espinosa. (Coordinadores)

Este libro es producto del trabajo desarrollado por un grupo interdisciplinario de investigadores integrantes del Instituto de Investigaciones Socioambientales, Educativas y Humanísticas para el Medio Rural (IISEHMER).
Libro gratis
Congresos

15 al 28 de febrero
III Congreso Virtual Internacional sobre

Desafíos de las empresas del siglo XXI

15 al 29 de marzo
III Congreso Virtual Internacional sobre

La Educación en el siglo XXI

Enlaces Rápidos

Fundación Inca Garcilaso
Enciclopedia y Biblioteca virtual sobre economía
Universidad de Málaga