domingo, 18 de mayo de 2014

Java static. Atributos y métodos estáticos o de clase (java)

Los atributos y métodos estáticos también se llamanatributos de clase y métodos de clase.
Se declaran como static.
Supongamos una clase Coche sencilla que se utiliza en un programa de compra-venta de coches usados y de la que se crean 3 objetos de tipo Coche:

La clase contiene 6 atributos: marca, modelo, color, matrícula, precio y descuento. Supongamos que el descuento es una cantidad que se aplica a todos los coches sobre el precio de venta. Como este dato es el mismo para todos los coches y es un valor que se puede modificar en cualquier momento no debe formar parte de cada coche sino que es un dato que deben compartir todos. Esto se consigue declarándolo como static.    

  
Un Atributo static:
-  No es específico de cada objeto. Solo hay una copia del mismo y su valor es compartido por todos los objetos de la clase.
- Podemos considerarlo como una variable global a la que tienen acceso todos los objetos de la clase.
- Existe y puede utilizarse aunque no existan objetos de la clase.
Para acceder a un atributo de clase se escibe:
NombreClase.atributo
Un Método static:
- Tiene acceso solo a los atributos estáticos de la clase.
- No es necesario instanciar un objeto para poder utilizarlo.
Para acceder a un método de clase se escribe:
NombreClase.método()
Ejemplo:

Vamos a escribir una clase Persona que contendrá un atributo contadorPersonas que indique cuantos objetos de la clase se han creado.
contadorPersonas debe ser un atributo de clase ya que no es un valor que se deba guardar en cada objeto persona que se crea, por lo tanto se debe declarar static:
static int contadorPersonas;
Un ejemplo de uso desde fuera de la clase Persona:
System.out.println(Persona.contadorPersonas);
Si lo declaramos como private:
private static int contadorPersonas;
solo podremos acceder al atributo desde fuera de la clase a través de métodos static:
public static void incrementarContador(){
        contadorPersonas++;
}
public static int getContadorPersonas() {
        return contadorPersonas;
}
En este caso un ejemplo de uso puede ser:
System.out.println(Persona.getContadorPersonas());

Cada vez que se crea una persona se incrementará su valor.
Si no es private, desde otra clase podemos hacerlo así:
Persona.contadorPersonas++;
Si es private, desde otra clase debemos incrementarlo así:
Persona.incrementarContador();


//Clase Persona
public class Persona {

    private String nombre;
    private int edad;
    private static int contadorPersonas;

    public Persona() {
    }

    public Persona(String nombre, int edad) {
        this.nombre = nombre;
        this.edad = edad;
    }

    public void setNombre(String nom) {
        nombre = nom;
    }

    public String getNombre() {
        return nombre;
    }

    public void setEdad(int ed) {
        edad = ed;
    }


    public int getEdad() {
        return edad;
    }

    public static int getContadorPersonas() {
        return contadorPersonas;
    }

    public static void incrementarContador() {
        contadorPersonas++;
    }
}

//Clase Principal
public class Estatico1 {
    public static void main(String[] args) {
        Persona p1 = new Persona("Tomás Navarra", 22);
        Persona.incrementarContador();
        Persona p3 = new Persona("Jonás Estacio", 23);
        Persona.incrementarContador();
        System.out.println("Se han creado: " + Persona.getContadorPersonas() + " personas");
    }
}
En lugar de utilizar el método incrementarContador() cada vez que se crea un objeto, podemos hacer el incremento de la variable estática directamente en el constructor.

El código de la clase Persona y de la clase principal quedaría ahora así:

//Clase Persona

public class Persona {

    private String nombre;
    private int edad;
    private static int contadorPersonas;

    public Persona() {
        contadorPersonas++;
    }

    public Persona(String nombre, int edad) {
        this.nombre = nombre;
        this.edad = edad;
        contadorPersonas++;
    }

    public void setNombre(String nom) {
        nombre = nom;
    }

    public String getNombre() {
        return nombre;
    }

    public void setEdad(int ed) {
        edad = ed;
    }

    public int getEdad() {
        return edad;
    }

    public static int getContadorPersonas() {
        return contadorPersonas;
    }
}

//Clase Principal
public class Estatico1 {

    public static void main(String[] args) {
        Persona p1 = new Persona("Tomás Navarra", 22);
        Persona p3 = new Persona("Jonás Estacio", 23);
        System.out.println("Se han creado: " + Persona.getContadorPersonas() + " personas");
    }
}

Método abstracto

Un método abstracto es un método declarado pero no implementado, es decir, es un método del que solo se escribe su nombre, parámetros y tipo devuelto pero no su código.

Los métodos abstractos se escriben sin llaves {} y con ; al final de la declaración.
Por ejemplo:
public abstract area();

Un método se declara como abstracto porque en ese momento (en esa clase) no se conoce cómo va a ser su implementación.
Por ejemplo: A partir de una clase Polígono se pueden derivar las clases Rectángulo y Triángulo. Ambas clases derivadas usarán un método área. Podemos declararlo en Figura como abstracto y dejar que cada clase lo implemente según sus necesidades.
Al incluir el método abstracto en la clase base se obliga a que todas las clases derivadas lo sobrescriban con el mismo formato utilizado en la declaración.
Si una clase contiene un método abstracto se convierte en clase abstracta y debe ser declarada como tal.

La forma general de declarar un método abstracto en Java es:
[modificador] abstract tipoDevuelto nombreMetodo([parámetros]);

sábado, 17 de mayo de 2014

Constructor (java)

Un constructor es un método especial de una clase que se llama automáticamente siempre que se declara un objeto de esa clase.
Su función es inicializar el objeto y sirve para asegurarnos que los objetos siempre contengan valores válidos.

Cuando se crea un objeto en Java se realizan las siguientes operaciones de forma automática:
1.  Se asigna memoria para el objeto.
2. Se inicializan los atributos de ese objeto con los valores predeterminados por el sistema.
3. Se llama al constructor de la clase que puede ser uno entre varios.
El constructor de una clase tiene las siguientes características:

Tiene el mismo nombre que la clase a la que pertenece.

En una clase puede haber varios constructores con el mismo nombre y distinto número de argumentos (se puede sobrecargar)

No se hereda.

No puede devolver ningún valor (incluyendo void).

Debe declararse público (salvo casos excepcionales) para que pueda ser invocado desde cualquier parte donde se desee crear un objeto de su clase.
MÉTODO CONSTRUCTOR POR DEFECTO
Si para una clase no se define ningún método constructor se crea uno automáticamente por defecto.
El constructor por defecto es un constructor sin parámetros que no hace nada. Los atributos del objeto son iniciados con los valores predeterminados por el sistema.
Por ejemplo, en la clase Fecha:
import java.util.*;
public class Fecha {

    private int dia;
    private int mes;
    private int año;
  
    private boolean esBisiesto() {
        return ((año % 4 == 0) && (año % 100 != 0) || (año % 400 == 0));
    }

    public void setDia(int d) {
       dia = d;
    }

    public void setMes(int m) {
        mes = m;
    }

    public void setAño(int a) {
        año = a;
    }

    public void asignarFecha() {
        Calendar fechaSistema = Calendar.getInstance();
        setDia(fechaSistema.get(Calendar.DAY_OF_MONTH));
        setMes(fechaSistema.get(Calendar.MONTH));
        setAño(fechaSistema.get(Calendar.YEAR));
    }

    public void asignarFecha(int d) {
        Calendar fechaSistema = Calendar.getInstance();
        setDia(d);
        setMes(fechaSistema.get(Calendar.MONTH));
        setAño(fechaSistema.get(Calendar.YEAR));
    }

    public void asignarFecha(int d, int m) {
        Calendar fechaSistema = Calendar.getInstance();
        setDia(d);
        setMes(m);
        setAño(fechaSistema.get(Calendar.YEAR));
    }

    public void asignarFecha(int d, int m, int a) {
        setDia(d);
        setMes(m);
        setAño(a);
    }

    public int getDia() {
        return dia;
    }

    public int getMes() {
        return mes;
    }

    public int getAño() {
        return año;
    }

    public boolean fechaCorrecta() {
        boolean diaCorrecto, mesCorrecto, anyoCorrecto;
        anyoCorrecto = (año > 0);
        mesCorrecto = (mes >= 1) && (mes <= 12);
        switch (mes) {
            case 2:
                if (esBisiesto()) {
                    diaCorrecto = (dia >= 1 && dia <= 29);
                } else {
                    diaCorrecto = (dia >= 1 && dia <= 28);
                }
                break;
            case 4:
            case 6:
            case 9:
            case 11:
                diaCorrecto = (dia >= 1 && dia <= 30);
                break;
            default:
                diaCorrecto = (dia >= 1 && dia <= 31);
        }
        return diaCorrecto && mesCorrecto && anyoCorrecto;
    }
}
no se ha definido ningún constructor, por lo que al declarar un objeto el compilador utilizará un constructor por defecto.
En este caso el método constructor por defecto es:
Fecha() { }
Vamos a añadir un constructor a la clase Fecha para poder iniciar los atributos de los nuevos objetos con valores determinados que se envían como parámetros. Si los valores corresponden a una fecha incorrecta se asigna la fecha del sistema:

public Fecha(int d, int m, int a) {
        dia = d;
        mes = m;  
        año = a;

        if (!fechaCorrecta()) {
            Calendar fechaSistema = Calendar.getInstance();
            setDia(fechaSistema.get(Calendar.DAY_OF_MONTH));
            setMes(fechaSistema.get(Calendar.MONTH));
            setAño(fechaSistema.get(Calendar.YEAR));
        }
}
Con este método constructor podremos crear objetos de la siguiente manera:
Fecha fecha1 = new Fecha(10,2,2011);
Se crea un objeto fecha1 y se le asignan esos valores a sus atributos.
Si se crea un objeto con una fecha no válida:
Fecha fecha2 = new Fecha(10,20,2011);
A dia, mes y año se les asignará los valores del sistema.
Java crea un constructor por defecto si no hemos definido ninguno en la clase, pero si en una clase definimos un constructor ya no se crea automáticamente el constructor por defecto, por lo tanto si queremos usarlo deberemos escribirlo expresamente dentro de la clase.
En este ejemplo hemos definido un método constructor por lo que también es necesario poner el método constructor por defecto.
public Fecha(){}
Este constructor se invocará cuando se declare un objeto sin parámetros.
La clase tiene ahora dos constructores por lo que podemos crear objetos Fecha de dos formas:
Fecha f1 = new Fecha();   //se ejecuta el constructor sin parámetros
Fecha f2 = new Fecha(1,1,2010); //se ejecuta el constructor con parámetros
INVOCAR A UN MÉTODO CONSTRUCTOR
Un constructor se invoca cuando se crea un objeto de la clase mediante el operador new.
Si es necesario invocarlo en otras situaciones, la llamada a un constructor solo puede realizarse desde dentro de otro constructor de su misma clase y debe ser siempre la primera instrucción.
Si tenemos varios métodos constructores en la clase, podemos llamar a un constructor desde otro utilizandothis.
Por ejemplo, si en el constructor con parámetros de la clase Fecha fuese necesario llamar al constructor sin parámetros de la misma clase escribimos:
public Fecha(int d, int m, int a) {
         this();  //llamada al constructor sin parámetros
         dia = d;
         ……….
}
CONSTRUCTOR COPIA
Se puede crear un objeto a partir de otro de su misma clase escribiendo un constructor llamado constructor copia.
Este constructor copia los atributos de un objeto existente al objeto que se está creando.
Los constructores copia tiene un solo argumento, el cual es una referencia a un objeto de la misma clase que será desde el que queremos copiar.
Por ejemplo, para la clase Fecha podemos escribir un constructor copia que permita crear objetos con los valores de otro ya existente:
Fecha fecha = new Fecha(1,1,2011);   //se invoca al constructor con parámetros
Fecha fecha1 = new Fecha(fecha);  //se invoca al constructor copia
El constructor copia que escribimos para la clase es:
//constructor copia de la clase Fecha
public Fecha(final Fecha f) {
        dia = f.dia;
        mes = f.mes;  
        año = f.año;
}
El constructor copia recibe una referencia al objeto a copiar y asigna sus atributos uno a uno a cada atributo del objeto creado.
Declarar el parámetro como final no es necesario pero protege al objeto a copiar de cualquier modificación accidental que se pudiera realizar sobre él.