Programación - RafaC - 2005/2006

Página Principal -

Tema 3: Definición de clases

1.- Definición de atributos
2.- Métodos y tipos de métodos
3.- Variables



1.- Definición de atributos

1.1 Declaración de atributos
1.2 Inicialización de atributos: constructoras
1.3 La variable this

1.1 Declaración de atributos

Los atributos son las variables que identifican a un objeto. Se deben elegir como atributos únicamente aquellos datos que permitan identificar al objeto y distinguir dos objetos de la misma clase. No deben incluirse como atributos variables auxiliares ni innecesarias, que deben representarse como variables locales en los métodos que las necesiten.

Los atributos se declaran dentro de la clase pero fuera de todos los métodos, precedidos (generalmente) por la palabra private, para protegerlos del acceso desde otras clases.


Ejemplos:

Para representar una clase Complejo representando un número complejo sobre el plano necesitamos dos atributos de tipo real (para la parte real e imaginaria). La clase comenzará así:

  public class Complejo {
     private double x; // parte real
	 private double y; // parte imaginaria
	 .....
  }
  

Para representar los datos una persona puede interesarnos:

	public class Persona {
	  private String nombre,apellidos,domicilio;
	  private int edad;
	  private double altura; 
	  .....	  
	}
	
Para representar un juego de las 3 en raya necesitaremos representar el tablero con las fichas además de saber en cada momento a cual de los dos jugadores corresponde jugar:

// clase para representar el juego de las tres en raya
public class TresEnRaya {
	
	// tipos auxiliares para representar los jugadores y los valores de la casilla
	private enum Jugadores {JUGADOR1,JUGADOR2};
	private enum Casilla {VACÍA,FICHAJUG1,FICHAJUG2};
	
	// tablero
	private Casilla tablero[][];
	
	// jugador que tiene el turno
	private Jugadores jugConTurno;
	
	.....
}

1.2 Inicialización de atributos: constructoras

Aunque Java da un valor por defecto (0 en caso de los números, null para los objetos), en el caso de los objetos nos puede interesar que al crearse se les dé unos valores diferentes de forma automática, o realizar algún otro tipo de tareas. Esto se realiza con un tipo especial de métodos, las constructoras, con la siguiente estructura:

public nombreClase(tipo1 arg1, ...., tipon argn) {
  ....
}


Observación: Las constructoras tienen 3 características que las distinguen del resto de los métodos:
Se llaman igual que la clase.
No tienen tipo de salida (ni siquiera void).
Sólo se llaman una vez por objeto, cuando éste se cre con new. No pueden llamarse explícitamente (sin new).

A la hora de diseñar una clase es muy importante definir de qué constructoras dispondrá la clase


Observación: Aunque no incluyamos ninguna constructora, toda clase tiene una constructora sin argumentos por defecto.

Ejemplo:

En la clase Complejo podemos pensar en dos constructoras: una sin argumentos que construye el complejo 0+0i, y otra que recibe los dos valores

  public class Complejo {
     private double x; // parte real
	 private double y; // parte imaginaria


     // constructoras
	 public Complejo() {x=y=0;}
	 
	 public Complejo(double laX, double laY) {
	    x = laX;	y = laY;
     }		
  }
  

Aviso: Una vez incluida una constructora con argumentos, la constructora sin argumentos deja de existir, por lo que si queremos seguir usándola hay que incluirla explícitamente, como en el ejemplo anterior.
Ejemplos de declaración de variables de tipo complejo:

Complejo c1 = new Complejo(); // c1 es el 0+0i
Complejo c2 = new Complejo(1,-3); // c2 es 1-3i

 

Para representar los datos una persona pueden interesarnos:

	public class Persona {
	  private String nombre,apellidos,domicilio;
	  private int edad;
	  private double altura; 
	  
	  // constructoras
	  public Persona(String elNombre, String losApellidos) {
          nombre = elNombre; apellidos=losApellidos;		  
	  }
	  
      public Persona(String elNombre, String losApellidos, int laEdad) {
          nombre = elNombre; apellidos=losApellidos; edad=laEdad;		  
	  }       
	}
	
Ejemplos de uso:
	
	Persona p1 = new Persona("Bertoldo","Gonsálvez de la Cuaderna");
	Persona p2 = new Persona("Casilda", "Bronciales", 22);
	
Sin embargo hay que recordar que una instrucción como:
	Persona anónima=new Persona();
	

daría error de compilación al no tener la clase Persona ninguna constructora sin parámetros.


En el juego de las 3 en raya hacemos que la constructora por defecto construya el tablero, con todas sus casillas vacías y fije el turno para el jugador 1:


// clase para representar el juego de las tres en raya
public class TresEnRaya {
	
	// tipos auxiliares para representar los jugadores y los valores de la casilla
	private enum Jugadores {JUGADOR1,JUGADOR2};
	private enum Casilla {VACÍA,FICHAJUG1,FICHAJUG2};
	
	// tablero
	private Casilla tablero[][];
	
	// jugador que tiene el turno
	private Jugadores jugConTurno;
	
	// constructora
	public TresEnRaya() {
		// creamos el array
		tablero = new Casilla[3][3];
		// lo inicializamos
		for (int i=0;i<3;i++)
		   for (int j=0; j<3; j++)
		         tablero[i][j] = Casilla.VACÍA;
		// fijamos el turno
		jugConTurno = Jugadores.JUGADOR1;         
	}	
}

Pregunta 3.a ¿Cómo se inicializará un objeto de la clase TresEnRaya? Muéstralo con un ejemplo

1.3 La variable this

En todas las clases existe implicitamente (i.e. declarado por el sistema, no por el usuario) un atributo privado de nombre this. Se trata de un atributo cuyo tipo es el la propia clase en la que está definido, y que contiene una referencia al propio objeto. Podemos imaginarlo así:

Gracias a this el objeto puede referirse a si mismo como si de un objeto más se tratara. Iremos viendo su utilidad poco a poco, pero podemos empezar por una de sus aplicaciones más comunes (aunque no la más interesante): permitir acceder a los atributos del objeto desde métodos cuyas variables locales lo hacen invisible. Veamos un ejemplo:



    public class Rara {
      private int x;
      
      public Rara() {
        int x = 20; // esta es otra variable x
        int y = X*x;

        // damos al atributo x el valor de y
        this.x = y;
      }
    }
    

En el cuerpo de esta constructora de esta clase se "mezclan" dos valores x:

El atributo x
La variable local x

Entonces, en la instrucción y=x*x; ¿ de cuál de las dos x se está hablando? La norma general en Java es que en caso de duda tiene preferencia la variable definida en un ámbito más próximo (esto se explicará con detalle después), por lo que se trata de la variable local. Para referirnos a la x atributo utilizamos la expresión this.x indicando de esta forma la x que es parte de este objeto (y que sólo puede ser un atributo, nunca una variable local).


Observación: A raíz de esto surge una pregunta ¿por qué no da el compilador error en this.x si x es una variable privada?. La respuesta es que Java permite a los objetos de una clase dada acceder a los componentes privados de otros objetos de la misma clase.

El ejemplo anterior puede parecer forzado; ¿no sería mejor simplemente evitar la repetición de nombres? Aunque esto es posible, a veces resulta más cómodo utilizar nombres repetidos. Un caso típico son los nombres de los argumentos que contienen los valores iniciales de los atributos, como en el siguiente ejemplo:

  public class Complejo {
     private double x; // parte real
	 private double y; // parte imaginaria


     // constructoras
	 public Complejo() {x=y=0;}
	 
	 public Complejo(double x, double y) {
	    this.x = x;	this.y = y;
     }
}

En este ejemplo la expresión this.x=x; asigna al atributo x (parte izquierda de la igualdad) el valor pasado como parámetro en la variable x (parte derecha de la igualdad). Haremos esto muy a menudo.


Observación: Algunos autores recomiendan escribir siempre la palabra this delante del nombre de un atributo, aunque no haya posibles ambigüedades.

Pregunta 3.b ¿Qué valor tomará el atributo x tras crearse un objeto de la clase cuyo código se muestra a continuación?

public class Rarísima {
	
	private int x;
	
	public Rarísima() {
		x = 5;		
		int x = 6;
		x = this.x + x;
		this.x += x;		
	}
}


2.- Métodos y tipos de métodos

2.1 Estructura de un método en Java
2.2 Valor de salida de una función
2.3 Objetos como valores de salida
2.4 Los métodos toString, equals y clone
2.5 Modificación de argumentos dentro de un método
2.6 Métodos estáticos
2.7 Sobrecarga

En este apartado vamos a estudiar los métodos, cómo devuelven valores y los tipos de métodos que podemos distinguir.

2.1 Estructura de un método en Java

La declaración de un método tiene siempre la siguiente estructura

cabeceraDelMétodo {
  cuerpoDelMétodo
}

La declaración de la cabecera de un método general perteneciente a una clase sería:

tipoAcceso static abstract final native synchronized
tipoSalida nombreMétodo(tipo1 arg1, ...., tipon argn)
throws listaExcepciones

Las palabras en itálica son opcionales, mientras que las que están en negrita son obligatorias. Normalmente la declaración se simplificará a algo como:

public tipoSalida nombre(tipo1 arg1, ...., tipon argn)
(public es uno de los tipos de acceso). En los siguientes apartados y en capítulos posteriores veremos muchas de las palabras opcionales con su significado. De momento, algunas observaciones:

Si la lista de argumentos es vacía no se pone nada entre paréntesis:
   
    public double valorX() {
       return x;
    }

Es obligatorio que la función tenga un tipo de salida. Si no queremos que la función devuelva nada, o bien queremos que lo haga a través de sus argumentos usaremos el tipo especial void como valor de salida. void es un tipo que no contiene ninguna constante, un tipo sin valores.

    public void saluda() {
       System.out.println("¿Qué tal?");
       return; // no es necesario, pero puede ponerse
    }
Tanto el tipo de salida como los tipos de los argumentos pueden referirse tanto a tipos simples como a objetos (i.e. a referencias a objetos).

En la lista de argumentos sólo se incluyen los nombres de los argumentos de llamada y su tipo, sin ningún calificador (ni var ni const ni &, ni nada). En los siguientes apartados veremos por qué.


2.2 Valor de salida de una función

Como acabamos de decir todos los métodos han de tener un valor de salida (el resultado de evaluar la función). Este valor se devuelve mediante la palabra reservada return seguida del valor deseado (que puede ser una expresión).

    public double xAlCuadrado() {
       return x*x;
    }    

Importante: El tipo de la expresión que se escribe tras return tiene que ser el mismo que el indicado como tipo de salida de la función en la cabecera.
Una excepción es el caso de una función de tipo void, en la que no hace falta poner la palabra return:

    public void cuentoCorto() {
       System.out.println("Nadie quería decirle a qué hora pasaría el tren.");
       System.out.println("Le veían tan cargado de maletas que les daba pena");
       System.out.println("explicarle que allí no había habido nunca");
       System.out.println("ni vías ni estación");
    }    
Aunque en este caso también puede escribirse, pero sin ninguna expresión al lado:

    public void beso() {
       System.out.println("Un beso de amor aparta el tiempo");
       return;
    }    

Pregunta 3.c ¿Qué escribirá una llamada al siguiente método?


    public void incompleto() {
       System.out.println("Las palabras más importantes...");
       return;
       System.out.println("nunca llegan a pronunciarse");
    }    


2.3 Objetos como valores de salida

En este apartado y en los siguientes vamos a utilizar la siguiente clase-ejemplo sencilla, que representa un número complejo:

Complejo.java



package complejos;

public class Complejo {

    // atributos, representan el número x+yi
    private double x,y;

    // constructoras
    public Complejo() { x=y=0;}
    public Complejo(double x, double y) {this.x=x; this.y=y;}

    // métodos
    public void  ponX(double nuevaX)  { x = nuevaX;}        
    public void ponY(double nuevaY) { y = nuevaY;}
    
    public double valorX() { return x;  }
    public double valorY() { return y; }

    public double módulo() {  return Math.sqrt(x*x + y*y); }  

    public String toString() { return x+"+"+y+"i";}    
 
}
Supongamos que queremos incluir un método en la clase Complejo que permita calcular el conjugado. Tenemos 3 posibilidades:
a) Declarar el método de tipo void. El estado del objeto quedará modificado.

    public class Complejo {
       private double x,y: // x es la parte real, y la imaginaria
       // por aquí irían los otros métodos de la clase 
       ....
       ....
       // convierte el número complejo en su conjugado
       public void conjugadoV1() {
          y = -y;                 
       }
     }  
    

Pregunta 3.d ¿Qué escribirá el siguiente programa?

Principal.java


    import complejos.Complejo;
    
    class Principal {
        public static void main(String [] args) {
           Complejo c = new Complejo();
           c.ponX(3):
           c.ponY(4);
           c.conjugadoV1();
           System.out.println(c.valorY());
         }  
     }             
     


b) Declarar el método de tipo Complejo, y hacer que el objeto no quede modificado, sino que devuelva un nuevo objeto con su conjugado.

    public class Complejo {
       private double x,y: // x es la parte real, y la imaginaria
       // por aquí irían los otros métodos de la clase 
       ....
       ....
       // crear un nuevo número complejo, conjugado del actual
       // el actual no varía 
       public Complejo conjugadoV2() {
         Complejo nuevo = new Complejo();
         nuevo.ponX(x);
         nuevo.ponY(-y);
         return nuevo; 
       }
     }  
    

Pregunta 3.e ¿Qué escribirá el siguiente programa? Principal.java

    import complejos.Complejo;

    public class Principal {
        public static void main(String [] args) {
           Complejo c = new Complejo();
           Complejo d;
           c.ponX(3):
           c.ponY(4);
           d = c.conjugadoV2();
           System.out.println(c.valorY());
           System.out.println(d.valorY());
         }  
     }             
     

c) Declarar el método de tipo Complejo, y hacer que el objeto quede modificado, y se devuelva a si mismo como resultado:

    package complejos;
    
    public class Complejo {
       private double x,y: // x es la parte real, y la imaginaria
       // por aquí irían los otros métodos de la clase 
       ....
       ....
       // crear un nuevo número complejo, conjugado del actual
       // el actual no varía 
       public Complejo conjugadoV3() {
         y = -y;
         return this; 
       }
     }  
    

Pregunta 3.f ¿Qué escribirá el siguiente programa?

Principal.java


    import complejos.Complejo;
 
    public class Principal {
        public static void main(String [] args) {
           Complejo c = new Complejo();
           Complejo d;
           c.ponX(3):
           c.ponY(4);
           d = c.conjugadoV3();
           System.out.println(c.valorY());
           System.out.println(d.valorY());
           c.ponX(-3);
           System.out.println(c.valorX());
           System.out.println(d.valorX());
           d.conjugadoV3();
           System.out.println(c.valorY());
           System.out.println(d.valorY());           
         }  
     }             
    


2.4 Los métodos toString, equals y clone

Se trata de métodos especiales que debe incluir toda clase. Cada uno tiene un propósito diferente.

toString Método sin argumentos que debe devolver un valor de tipo String. Es utilizado cuando hay que convertir un objeto en un valor de tipo String. Por ejemplo, si pasamos un objeto al método System.out.println, éste usará el método toString para obtener la cadena de caracteres a mostrar.

Ejemplo:

Supongamos que en la clase Complejo añadimos la siguiente definición del método toString():

public String toString() { return x+"+"+y+"i";}

Entonces podemos probar un programa como el siguiente:

 

import complejos.Complejo;

public class Principal {
    public static void main(String[] args) {
    	Complejo c = new Complejo(3,4);
    	System.out.println(c); // es lo mismo que escribir System.out.println(c.toString());    	
    }
}
	

que escribirá por pantalla 3.0+4.0i


Aviso: Un error común es pensar que toString tiene que escribir algo por pantalla. Sólo tiene que devolver un valor de tipo String

Aviso: Si no definimos un método toString el sistema incluye uno por defecto, que se encargará de devolver una cadena de caracteres representando la referencia contenida en el tipo objeto. Por ejemplo, si no incluímos ningún método toString en la clase Complejo, el programa anterior escribirá algo similar a:

complejos.Complejo@10b62c9


equals Como vimos en el tema anterior no se debe usar el operador == para comparar objetos, ya que esta operación sólo comparará las referencias a dichos objetos, devolviendo true si se trata de dos referencias al mismo objeto, o false en caso contrario. Por ejemplo un programa como:

import complejos.Complejo;

public class Principal {
    public static void main(String[] args) {
  
    	Complejo c1 = new Complejo(3,4);
    	Complejo c2 = new Complejo(3,4);
    	System.out.println(c1==c2); 
    }
}
	

escribirá false porque c1 y c2 se trata de dos referencias a objetos diferentes. Si lo que deseamos es saber si los dos objetos contienen el mismo número complejo se debe utilizar el método equals, que debe ser definido de la forma adecuada en la cada clase. En el caso particular de la clase Complejo, un posible método equals sería:

    public boolean equals(Complejo c) {
    	return this.x == c.x && this.y==c.y;
    }
	
Una vez definido este método podremos escribir:
import complejos.Complejo;
public class Principal {
    public static void main(String[] args) {
  
    	Complejo c1 = new Complejo(3,4);
    	Complejo c2 = new Complejo(3,4);

    	System.out.println(c1.equals(c2)); 
    }
}
	
que escribirá ahora true como esperábamos.

Aviso: Igual que ocurri ía con toString, el sistema incluye un método equals por defecto, pero su comportamiento es el de == por lo que siempre conviene que escribamos nuestro propio método en cada clase.



Aviso: Siendo precisos, la definición de equals del ejemplo para la clase Complejo no es del todo correcta: Java pide que equals sea capaz de comparar un objeto con cualquier otro, sea o no de su misma clase, mientras que el código que hemos escrito sólo permite comparaciones con otro número complejo. En capítulos posteriores mejoraremos la defición para que el método admita objetos de otros tipos (en cuyo caso devolverá true ).



clone También hemos visto en el capítulo precedente que una expresión de la forma c1=c2; con c1, c2 objetosm no hace que c2 sea una copia de c1, sino una nueva referencia a c1. Para conseguir crear nuevos objetos que sean copias de objetos ya existentes debemos definir y utilizar el método clone. Una posible definición de clone para la clase Complejo sería:

  public Complejo clone() {
    	return new Complejo(this.x,this.y);
    }
  

Una vez definido este método podemos crear copias de objetos ya existentes:

 

import complejos.Complejo;
public class Principal {
    public static void main(String[] args) {
  
    	Complejo c1 = new Complejo(3,4);
    	Complejo c2 = c1.clone();
    	c2.ponX(7.0);
    	
    	System.out.println(c1); 
    	System.out.println(c2); 
    }
}
  

Pregunta 3.g ¿Qué escribirá el programa anterior? ¿Qué escribiría si en lugar de Complejo c2 = c1.clone(); pusiéramos Complejo c2 = c1;?

2.5 Modificación de argumentos dentro de un método

En Java, como en la mayoría de los lenguajes, el valor que se pasa como argumento es copiado a la variable que se escribe en la cabecera del método.

Observación: se puede pensar que las variables que aparecen en la cabecera son variables locales a la función, con la particularidad de que reciben sus valores desde fuera, mediante los valores de llamada.

Los argumentos de tipos básicos no quedan modificados aunque se modifiquen en un método, no importa si de la propia clase o de una clase diferente:


Tonto.java

    public class Tonto {
        public static void main(String [] args) {
           int x=5;
           incrementa(x);
           System.out.println(x);
        }   
        
        public static void incrementa(int a) {
          a =a+1;
        }
     }
     


Pregunta 3.h ¿Qué escribirá el programa anterior?

Para lograr que se incremente la variable se podría hacer:

 

Inc.java

    public class Inc {
        public static void main(String [] args) {
           int x=5;
           x = incrementa(x);
           System.out.println(x);
        }   
        
        public static int incrementa(int a) {
          a =a+1;
          return a;
        }
     }
     

En el caso de los objetos se procede de igual forma; se copia el contenido de la variable a la variable local declarada en la cabecera del método. Sin embargo hay una diferencia muy importante:

Observación: como hemos visto las variables de tipos no básico no contienen en realidad los objetos, sino referencias a los objetos. La copia a la variable de un método será de la referencia, no del objeto, y por tanto podremos modificar el objeto.

Esto se observa en el siguiente ejemplo:

 

Intercambio.java

    import complejos.Complejo;
    
    public class Intercambio {
        public static void main(String [] args) {
           Complejo c = new Complejo();
           c.ponX(3);
           c.ponY(2);
           intercambia(c);
           System.out.println(c.valorX());
           System.out.println(c.valorY());
           
        }   

        public static void intercambia(Complejo c) {
          // 2 variables locales con las partes reales e imaginarias de c
          double x = c.valorX();
          double y = c.valorY();
          // ponemos en x el valor y, y viceversa
          c.ponX(y);
          c.ponY(x);
        }
     }
     


Pregunta 3.i ¿Qué escribirá el programa anterior?


Y aún un ejemplo más:

Raro.java

    import complejos.Complejo;
    
    public class Raro {
        public static void main(String [] args) {
           Complejo c = new Complejo();
           c.ponX(3);
           c.ponY(2);
           ponACeroV1(c);
           System.out.println(c.valorX());
           System.out.println(c.valorY());
           ponACeroV2(c);
           System.out.println(c.valorX());
           System.out.println(c.valorY());
           
        }   

        public static void ponACeroV1(Complejo c) {
          Complejo a = new Complejo();
          a.ponX(0);
          a.ponY(0);
          c = a;
        }

        public static void ponACeroV2(Complejo c) {
          c.ponX(0);
          c.ponY(0);
        }
        
     }
     


Pregunta 3.j ¿Qué escribirá el programa anterior?

Observación: Cuando un objeto se queda sin referencias que lo apunten es destruido por el sistema y su memoria liberada. Es lo que en Java se llama recogida automática de basura.

El siguiente programa trata de ilustrar esta idea:

 

Basura.java
import complejos.Complejo;
    
    public class Basura {
        public static void main(String [] args) {
           Complejo a;
 1           a = new Complejo();
 2           a.ponX(1);
 3           a.ponY(1);
 4           Complejo b = new Complejo();           
 5           b.ponX(3);
 6           b.ponY(2);
 7           Complejo c = a;
 8           a = b;
 9           b = c;
10           a = new Complejo();
11           a.ponX(2);
12           a.ponY(2);           
        }   
     }
     


Pregunta 3.k Indica qué valor toman las variables en cada fase del programa y cuándo se crea y se puede destruir cada objeto.

Al igual que sucede con los objetos, los arrays se pasan por referencia a los métodos. Por tanto si un método modifica el contenido de un array éste queda modificado también fuera del método.


Principal.java

import complejos.Complejo;

public class Principal {
    public static void main(String[] args) {
        
    int i;
    Complejo  []puntos = { new Complejo(1,2), new Complejo(6,7)}; 

    muestra(puntos);
    incrementaXs(puntos); 
    muestra(puntos);

    Complejo []puntos2 = {new Complejo(1,2), new Complejo(6,7)};
    
    muestra(puntos2);
    tresCeros(puntos2);
    muestra(puntos2);        
    }  // main
    
    public static void muestra(Complejo []t) {
        for (int j=0; j<t.length; j++)
           System.out.println(t[j]);
    }
    
    public static void incrementaXs(Complejo []t) {
        for (int j=0; j<t.length; j++)
            t[j].ponX( t[j].valorX()+1);
    }
    
    
    public static void tresCeros(Complejo []t) {
        t = new Complejo[3];
        for (int j=0; j<t.length; j++) 
            t[j] = new Complejo(0,0);
        muestra(t);    
    }  
}


Pregunta 3.l ¿Qué escribirá el programa anterior?


2.6 Métodos estáticos

En una primera aproximación podemos dividir los métodos en 4 grupos:

Métodos que inicializan el objeto o constructoras.
Métodos que permiten consultar el estado del objeto.
Métodos que permiten modificar el estado del objeto.
Métodos que independientes del estado del objeto o estáticos.

Hasta ahora hemos visto constructoras, así como métodos para consultar y para modificar el estado del objeto. Vamos a estudiar ahora los métodos estáticos.

Los métodos estáticos, son métodos cuyo resultado o efecto es independiente de los atributos que pueda tener un objeto de esta clase, y por tanto del objeto en sí. A menudo se encuentran en clases sin atributos, y con todos los demás metodos también estáticos.

Operaciones.java
    package operaciones;
    
    public class Operaciones {
        public static int cuadrado(int x) {
          return x*x;
        }
        public static int cubo(int x) {
          return x*x*x;
        }
     }   
   

Los métodos estáticos pueden utilizarse sin declarar ningún objeto, directamente como parte de la clase:

 

Principal.java
    import operaciones.Operaciones;
    
    public class Principal {
        public static void main(String [] args) {
           int a,b;
           
           // esto estaría mal si "cuadrado" no fuera estático porque "Operaciones" 
           // no es un objeto sino una clase.
           a = Operaciones.cuadrado(3);
           b = Operaciones.cubo(4);
           System.out.println(a);
           System.out.println(b);
        }
     }   
     


Observación: El uso de métodos estáticos "rompe" la filosofía de Java y sólo deben usarse en casos muy especiales. Sí se utilizan bastante las constantes estáticas.

2.7 Sobrecarga

Algunas clase (como Complejo) tienen dos o más constructoras, todas con el mismo nombre (el de la clase) pero con distinto número de argumentos. Esto no es específico de las constructoras, sino que se puede hacer con cualquier método, como por ejemplo:

Incrementos.java
    package operaciones;
    
    public class Incrementos {
        private int x;
        
		public Incrementos(int laX) {
          x = laX;
        }
        
        public int f(int a) {
          return a+x;
        }
        
        public int f(int a, int b) {
          return a+b+x;
        }
     }   
     

A la hora de utilizar un método u otro el sistema se fija en el número y tipo de los parámetros y en el valor de salida:


Principal.java
    import operaciones.Incrementos;
    
    public class Principal {
        public static void main(String [] args) {
           Incrementos  v = new Incrementos(3);
           System.out.println(v.f(1));
           System.out.println(v.f(2,3));
     

A esta posibilidad se la conoce con el nombre de sobrecarga, y a menudo se considera una de las características típicas de la programación orientada a objetos.

3.- Variables dentro de objetos

3.1 Ámbitos
3.2 Constantes y variables estáticas

3.1Ámbitos

Se llama ámbito a la región de código en la que una variable está definida. Inicialmente distinguimos 3 ámbitos para las variables en Java:

Atributos, globales a toda la clase.
Variables locales de un método.
Parámetros de un método.

Veamos cada uno por separado:


Atributos

Los atributos se declaran al comienzo de la clase (normalmente) y duran todo el tiempo que "vive" el objeto. Son visibles por todos los métodos del programa siempre y cuando éstos no tengan un parámetro o variable local que los oculte. Pueden inicializarse en la declaración para darles un valor diferente del valor por defecto:

    public class .... {
      private Complejo c= new Complejo(1.1,2);
      private int a = 2;
      
      public int f(int x) {
       return x+a+c.valorX();
      }
    }
   
Variables locales

Sólo existen desde que se declaran hasta que el método se acaba:

    public class .... {
      
      public int f(int x) {
       int a=5;
       a = a + x;
       return a;
      }
      
      public void g() {
       System.out.println(f(2));
       System.put.println(f(3));
      }
    }
  

Pregunta 3.m ¿Qué escribirá una llamada a la función g()?
Parámetros de un método:
Siguen las mismas normas que para las variables locales, con la diferencia de que no se pueden inicializar; se inicializan automáticamente con el valor que se les pasa como argumento.

Ejemplo:
  
    public class .... {
      private int x=3;
      
      public void f(int x) {
        System.out.println(x+1)
      }
      
      public void g() {
       f(x);
      }
    }  


Pregunta 3.n ¿Qué escribirá una llamada a la función g()?

3.2 Constantes y variables estáticas


A menudo interesa incluir en las clases constantes que sean visibles para el usuario de la clase. Dichas constantes no dependen normalmente de los atributos del objeto en particular, y serán por tanto estáticas. Además deben ser públicas para que puedan ser utilizadas. Por ejemplo supongamos que queremos incluir en la clase Complejo una constante con el número imaginario i. Podríamos entonces escribir:

 

Complejo.java
    package complejos;
    
    public class Complejo {

        private double x,y;
        public static final Complejo i = new Complejo(0,1);

         // por aquí irían los métodos que hemos declarado hasta ahora.
         ....
    }
    

y utilizar esta constante como parte de la clase:

 

ConComplejos.java
    import complejos.Complejo;
    
    class ConComplejos {
        public static void main(String args[]) {
        System.out.println(Complejo.i.módulo());
        }
    }
    

Pregunta 3.o ¿Qué escribirá el programa anterior?


Página Principal-


2005-2006 Rafael Caballero