Modificador Static
Introducción
Inaguramos esta parte de conceptos de orientación a objetos más avanzados con la palabra reservada static. Vamos a tener:
- Variables static.
- Métodos static.
La idea de estas variable o métodos pertenecen a la clase en sí, no a un objeto en concreto (cómo lo visto hasta ahora). Es como si fueran una variable “global” para todos los objetos de una clase. Pero realmente forma parte de la Clase, de una manera “estática”. No de lo dinámico de los objetos, que cada objeto tiene sus atributos y métodos con sus valores, estados y momentos de ejecución.
Gráfico explicativo
Variables Static
Serán atributos de la clase, también se le llama una constante de clase (pero se puede variar el contenido), o atributos comunes a todos los objetos de la clase. Todos los objetos de la misma clase pueden acceder a los datos static, pero incluso, aunque la clase no tenga objetos, estos datos existen y son utilizables a través del nombre de la clase. Como cabría esperar, los datos static no se almacenan con los objetos si no en una zona de memoria especial para dichos datos.
Utilidad clara -> llevar la cuenta de cuantos objetos se están creando:
class Cohete{
static int numCohetes=14;
String nombre;
Cohete(String nombre){
numCohetes++;
this.nombre=nombre;
}
}
class Unidad4 {
public static void main(String[] args) {
System.out.println(" total de cohetes "+Cohete.numCohetes);
}
}
Tal como véis en el anterior ejemplo, se puede inicializar las variables tipo static. Esta inicialización se realizar al cargar la clase en memoria, no al crear objetos de la misma por primera vez. Ojo! -> las variables locales no se pueden declarar “static”.
Formas de acceder a esa variable estática
Existen múltiples formas pero la que se interpreta más “natural” es através del nombre de la clase:
Cohete.numCohetes
Pero se tiene acceso desde cualquier objeto de la clase:
Cohete c1=new Cohete("Apollo 1");
System.out.println("nombre de cohete c1:"+c1.nombre+" y numero total de cohetes "+c1.numCohetes);
Cohete c2=new Cohete("Apollo 2");
System.out.println("nombre de cohete c2:"+c2.nombre+" y numero total de cohetes "+c1.numCohetes);
Cohete c3=new Cohete("Apollo 3");
System.out.println("nombre de cohete c3:"+c3.nombre+" y numero total de cohetes "+c1.numCohetes);
System.out.println("numero total de cohetes con c1: "+c1.numCohetes);
System.out.println("numero total de cohetes con c2: "+c2.numCohetes);
System.out.println("numero total de cohetes con c3: "+c3.numCohetes);
System.out.println("numero total de cohetes con Cohete: "+Cohete.numCohetes);
Ejercicio 1
Añade a la clase Punto la capacidad de controlar el número de objetos Punto creados
class Punto {
int x , y ;
Punto ( int x, int y ) {
this.x = x ;
this.y = y;
}
}
Ejercicio 2
Intenta escribir un programa para representar el consumo de energía de una instalación eléctrica. Para ello, se hará una clase que representa los aparatos conectados en la instalación. Cada aparato tiene un consumo eléctrico determinado. Al encender un aparato eléctrico, el consumo de energía se incrementa en la potencia de dicho aparato. Al apagarlo, se decrementa el consumo. Inicialmente, los aparatos están todos apagados. Además se desea consultar el consumo total de la instalación.
Dicho todo ésto, haz un programa que declare dos aparatos eléctricos, una bombilla de 150 watios y una plancha de 2000 watios. El programa deberá imprimir el consumo nada más crear los objetos. Después, se enciende la bombilla y la plancha, y el programa imprime el consumo. Luego se apaga la bombilla, y se vuelve a imprimir el consumo.
Bloque estáticos
Es un bloque que podemos definir en una clase para inicializar las variables estáticas. De la misma forma cuando usamos los constructores con variable normales. Sólo se pueden usar variables estáticas dentro del bloque, y puede haber más de uno -> el orden de ejecución será el orden en el código de la clase.
class Test{
static {
//Codigo iria aqui
}
}
public class Demo {
static int a;
static int b;
static {
a = 10;
b = 20;
}
public static void main(String args[]) {
System.out.println("Value of a = " + a);
System.out.println("Value of b = " + b);
}
}
Recordad que lo estático se ejecuta al cargar la clase en memoria no al crear objetos de ella.
Métodos Static
Es la misma idea que con variables estáticas, en este caso serán métodos globales a una clase, que desde la clase o cualquier instancia de ella (objetos) podemos ejecutar:
class Impuesto{
static int valormax=200;
static int valormaxDiv2(){
return valormax/2;
}
}
public class Unidad4 {
public static void main(String[] args) {
System.out.println("valormax: "+ Impuesto.valormax);
System.out.println("valormax dividido por 2: "+ Impuesto.valormaxDiv2());
Impuesto.valormax=6000;
System.out.println("valormax: "+ Impuesto.valormax);
System.out.println("valormax dividido por 2: "+ Impuesto.valormaxDiv2());
}
}
Desde el ejemplo anterior probar a crear objetos y ejecutar el método estático desde los mismos. Aspectos importantes a tener en cuenta es que desde métodos estáticos no podemos usar this, por motivos obvios. Y desde los métodos estáticos no pueden acceder a atributos que no sean static.
Ejercicio 3
Observa el siguiente ejemplo:
class Racional{
int numerador;
int denominador;
Racional(int numerador, int denominador){
this.numerador=numerador;
this.denominador=denominador;
}
void multiplicar(Racional r1, Racional r2){
this.numerador=r1.numerador*r2.numerador;
this.denominador=r1.denominador*r2.denominador;
}
}
class Unidad4{
public static void main(String[] args) {
Racional r1=new Racional(3,4);
Racional r2=new Racional(1,2);
Racional r3=new Racional(1,1);
r3.multiplicar(r1, r2);
System.out.println("MUTIPLICACIÓN DE NÚMEROS RACIONALES");
System.out.println("r1 vale: "+r1.numerador+"/"+r1.denominador);
System.out.println("r2 vale: "+r2.numerador+"/"+r2.denominador);
System.out.println("r3 vale: "+r3.numerador+"/"+r3.denominador);
}
}
Modifica el ejemplo anterior, de forma que ahora la multiplicacion se puedan usar de la siguiente forma: r3=Racional.multiplicar(r1,r2);
Ejercicio 4
Observa el siguiente ejemplo ya hecho:
class Potencia{
int elevar(int base,int exponente){
int resultado=1;
for(;exponente>0;exponente--){
resultado=resultado*base;
}
return resultado;
}
}
class Unidad4{
public static void main(String[] args) {
Potencia p = new Potencia();
System.out.println(p.elevar(2,1));
System.out.println(p.elevar(5,3));
System.out.println(p.elevar(9,0));
}
}
Reescribe el ejemplo anterior para que funcione el siguiente main:
public static void main(String[] args) {
System.out.println(Unidad4.pot(2,1));
System.out.println(pot(5,3));
System.out.println(new Unidad4().pot(9,0));
}
Ejercicio 5
Construye una clase Complejo con dos atributos:
● real: parte real del número complejo ● imag: parte imaginaria del número complejo
Puedes consultar la estructura de una clase en el apartado correspondiente de la unidad, o bien partir de la definición de la clase Persona del apartado anterior. A continuación crea los siguientes métodos dentro de la clase:
- public Complejo(): Constructor que inicializa los atributos a cero.
- public Complejo(double real, double imag): Constructor que inicializa los atributos a los valores indicados por los parámetros.
- public double getReal(): Devuelve la parte real del objeto.
- public double getImag(): Devuelve la parte imaginaria del objeto.
- public void setReal(double real): Asigna a la parte real del objeto el valor indicado en el parámetro real.
- public void setImag(double imag): Asigna a la parte imaginaria del objeto el valor indicado en el parámetro imag.
- public String toString(): Convierte a String el número complejo, mediante la concatenación de sus atributos y devuelve como resultado la cadena de texto 3 + 4i, si 3 es la parte real y 4 la parte imaginaria.
- public void sumar(Complejo b): Suma la parte real con la parte real del número complejo b y la parte imaginaria con la parte imaginaria del número complejo b. La clase Complejo pertenecerá al paquete llamado numeros. Crea otra clase denominada DemoNum, dentro del mismo paquete, que pruebe todos los métodos de la clase Complejo.
Conclusiones
- No se debe abusar de lo “estático” porque el paradigma O.O. se debilitaría.
- Muchas librerías y clases para ejecutarlas es necesario ejecutar sus métodos estáticos: Math, Integer.parseInt() etc…
- Fijaros que el main es static -> no es necesario crear una instancia de esa clase principal.
- Los métodos static no pueden usar la referencia this.
- Un método static no puede acceder a miembros(de su clase) que no sean static
- Un método no static puede acceder a miembros static y no static.