Curso basico de Java - #12 - POO: Static y modificadores de acceso

Muy buenas,
Como se menciona en el anterior tema, vamos a ver dos elementos esenciales de la POO en Java; static y los modificadores de acceso (public, private, protected).


Static
En el anterior tema vimos el ejemplo de una clase; Manzana. Esta clase tenia varios atributos (peso_kg, color, …) y métodos (getAtributos(), comer()). Por cada objeto que creamos de esta clase, los atributos y métodos eran individuales; independientes entre si.
Siendo así que un objeto puede tener color = "Rojo" y otro puede tener color = Verde y coexistir al mismo tiempo.

Pero, ¿que pasa si queremos un atributo o método compartido? El cual no sea necesario usar a trabes de un objeto. Pues usamos elementos estáticos (static).

Siguiendo el ejemplo anterior, nos podría interesar saber cuantas manzanas se han instanciado, por lo que necesitaríamos un contador que no perteneciera a un objeto, sino que a la clase:

Manzana.java
/**
 * Clase Manzana.
 * Contempla los estados que puede tener una manzana para poder crear objetos diferentes.
 * Tiene como atributos el peso (Kg), el color y si esta comida de una manzana
 */
public class Manzana
{
	/* Atributos */
	float   peso_kg;
	String  color;
	boolean estaComida;
	
	static int cantidadDeManzanas; // Contador estático de instancias de manzanas.
	
	/**
	 * Constructor.
	 * Recibe el peso y el color de la manzana y se lo asigna a los atributos del objeto.
	 * Por defecto las manzanas están sin comer.
	 * @param p (float) Peso de la manzana en Kg.
	 * @param c (String) Color de la manzana. Ej: "Rojo".
	 */
	public Manzana(float p, String c)
	{
		peso_kg    = p;
		color      = c;
		estaComida = false;

		cantidadDeManzanas++;  // Cuando el constructor sea llamado la cantidad aumentara en 1.
	}
	
	/**
	 * Devuelve el valor actual de los atributos del objeto.
	 * @return (String) Cadena con el valor de todos los atributos.
	 */
	public String getAtributos()
	{
		String manzanaComida;  // Variable para guardar si la manzana ha sido comida o no en String.
		
		// Asignar el valor que le corresponde a manzanaComida dependiendo del valor booleano de estaComida.
		if (estaComida)
		{
			manzanaComida = "Si";
		}
		else
		{
			manzanaComida = "No";
		}
		
		// Devolver cadena con los atributos del objeto.
		return "Peso: " + peso_kg + "Kg. Color: " + color + ". ¿Esta comida?: " + manzanaComida + ".";
	}
	
	/**
	 * Simula la acción de comer la manzana, cambiara su estado a "comida" mediante la variable booleana estaComida.
	 */
	public void comer()
	{
		estaComida = true;
	}
}

Si ahora creamos varios objetos de la clase Manzana, veremos como el valor de cantidadDeManzanas va incrementando:

AdministaManzanas.java
public class AdministaManzanas
{
	public static void main(String[] args)
	{
		Manzana miManzana1 = new Manzana(0.35F, "Verde");
		System.out.println(Manzana.cantidadDeManzanas);

		Manzana miManzana2 = new Manzana(0.35F, "Verde");
		System.out.println(Manzana.cantidadDeManzanas);

		Manzana miManzana3 = new Manzana(0.35F, "Verde");
		System.out.println(Manzana.cantidadDeManzanas);
	}
}

Para acceder al un atributo de un objeto o clase, se usa la sintaxis [objeto|clase].[atributo]. Esto es algo que veremos a continuación mas en profundidad ya que es muy relevante de cara a los modificadores de acceso.

Para atributos o cualquier otro elemento estático (de clase, no de objeto) es aconsejable llamarlos desde la propia clase (Manzana.cantidadDeManzanas), aunque, desde un objeto de la clase también es posible (miManzana.cantidadDeManzanas;), no se recomienda.

Eclipse se queja.

Output
1
2
3

Modificadores de acceso
Como he mencionado en otras ocasiones, los modificadores de acceso sirven para controlar desde que clase son accesibles ciertos elementos. Esto es muy importante para garantizar la adecuada encapsulación de los datos y funciones de un código.

En Java hay 3-4 tipos, veamos cuales son y desde donde son accesibles los elementos a los que se les ponga:

Modificador Desde donde es accesible
public Desde cualquier clase.
private Unicamente desde la clase donde se encuentra.
protected Desde la misma clase, clases del mismo paquete, y subclases incluso si están en otros paquetes.
Por defecto (sin modificador) Desde clases del mismo paquete solamente.

Ya veremos mas adelante eso de los paquetes, no alarmarse.

*Las clases principales (las del archivo .java) pueden ser public o por defecto.
Las clases internas (no confundir con las sub-clases) si que pueden tener los demás modificadores de acceso.


Ejemplo de clase encapsulada
Por lo general, los elementos de las clases se encapsulan, por lo que vamos usar el ejemplo de la clase Manzana, le añadiremos varios modificadores de acceso y le crearemos métodos getters y setters.

Manzana.java
/**
 * Clase Manzana.
 * Contempla los estados que puede tener una manzana para poder crear objetos diferentes.
 * Tiene como atributos el peso (Kg), el color y si esta comida de una manzana
 */
public class Manzana
{
	/* Atributos */
	private float   peso_kg;
	private String  color;
	private boolean estaComida;
	
	private static int cantidadDeManzanas; // Contador estático de instancias de manzanas.
	
	/**
	 * Constructor.
	 * Recibe el peso y el color de la manzana y se lo asigna a los atributos del objeto.
	 * Por defecto las manzanas están sin comer.
	 * @param p (float) Peso de la manzana en Kg.
	 * @param c (String) Color de la manzana. Ej: "Rojo".
	 */
	public Manzana(float p, String c)
	{
		peso_kg    = p;
		color      = c;
		estaComida = false;

		cantidadDeManzanas++;  // Cuando el constructor sea llamado la cantidad aumentara en 1.
	}
	
	/**
	 * Devuelve el valor actual de los atributos del objeto.
	 * @return (String) Cadena con el valor de todos los atributos.
	 */
	public String getAtributos()
	{
		String manzanaComida;  // Variable para guardar si la manzana ha sido comida o no en String.
		
		// Asignar el valor que le corresponde a manzanaComida dependiendo del valor booleano de estaComida.
		if (estaComida)
		{
			manzanaComida = "Si";
		}
		else
		{
			manzanaComida = "No";
		}
		
		// Devolver cadena con los atributos del objeto.
		return "Peso: " + peso_kg + "Kg. Color: " + color + ". ¿Esta comida?: " + manzanaComida + ".";
	}
	
	/*Getters*/
	
	/**
	 * Devuelve el peso de la manzana en Kg.
	 * @return (float) Peso en Kg.
	 */
	public float getPeso()
	{
		return peso_kg;
	}
	
	/**
	 * Devuelve el color de la manzana.
	 * @return (String) Color de la manzana.
	 */
	public String getColor()
	{
		return color;
	}
	
	/**
	 * Indica si la manzana ya fue comida.
	 * @return (boolean) true si la manzana fue comida, false en caso contrario.
	 */
	public boolean getEstaComida()
	{
		return estaComida;
	}
	
	/**
	 * Devuelve la cantidad total de manzanas instanciadas.
	 * Es estatic porque no se necesita ningun objeto para llamara a este metodo.
	 * @return número de manzanas creadas.
	 */
	public static int getCantidadDeManzanas()
	{
		return cantidadDeManzanas;
	}
	
	
	/*Setters*/
	
	/**
	 * Establece el peso de la manzana en Kg.
	 * @param p (float) Peso en Kg.
	 */
	public void setPeso(float p)
	{
		peso_kg = p;
	}
	
	/**
	 * Establece el color de la manzana.
	 * @param c (String) Color deseado.
	 */
	public void setColor(String c)
	{
		color = c;
	}
	
	/* 
	 * No nos interesa crear un setter de cantidadDeManzanas porque 
	 * solo queremos que se incremente en uno cuando se instancia un nuevo objeto.
	 */
	
	/**
	 * Simula la acción de comer la manzana, cambiara su estado a "comida" mediante la variable booleana estaComida.
	 */
	public void comer()
	{
		estaComida = true;
	}
	
}

Mas o menos vamos sentando las bases para la POO.
Para poner en practica todos estos conocimientos, en el siguiente tema veremos un ejemplo mas amplio.

Ademas, y repitiendo lo que dije en el tema anterior, se que estos conceptos pueden ser complejos de digerir para los que no son programadores, así que animo a preguntar cualquier duda que le surja a cualquier persona que este leyendo esto.
Sobre todo animo a que programéis y experimentéis, que siempre es la mejor forma de aprender.

1 me gusta