1. Introducción
En Java, como en la vida, no todo debe estar disponible para todo el mundo en todo momento. Imagina un piso: no querrías que los vecinos pasearan por tu dormitorio, ¿verdad? En los programas, a veces queremos «cerrar la puerta» a ciertas variables o métodos para que no se pueda acceder a ellos desde fuera.
Para ello, en Java existen los modificadores de acceso: palabras especiales que indican dónde se puede usar una u otra variable o método, por ejemplo, public o private.
Modificadores de acceso principales
| Modificador | ¿Dónde es visible? |
|---|---|
|
En cualquier lugar donde sea visible la clase (en otros paquetes y en otros archivos) |
|
Solo dentro de la misma clase |
|
Solo dentro del mismo paquete (es decir, dentro de clases en el mismo directorio) |
Existen más modificadores, pero hablaremos de ellos más adelante, cuando lleguemos a la herencia.
Ejemplo práctico
public class User
{
public String name; // visible para todos
private int age; // visible solo dentro de la clase User
public void sayHello()
{
System.out.println("Hola, me llamo " + name);
}
private void secretMethod()
{
System.out.println("¡Es un método secreto!");
}
}
Explicación:
- name — es visible en cualquier sitio donde se pueda usar la clase User.
- age — es visible solo dentro de la propia clase User.
- sayHello() — es un método público; se puede llamar desde cualquier otra clase.
- secretMethod() — es un método privado; no se puede invocar desde fuera.
¿Cómo funciona esto en tareas reales?
Supongamos que tenemos una clase que describe una cuenta bancaria. Claramente no queremos que cualquiera pueda cambiar el saldo directamente. Por eso, la variable del saldo será private y habrá métodos específicos para trabajar con ella.
¿Por qué no conviene hacerlo todo public?
Puede surgir la tentación: «¡Hagamos todo public para no complicarnos!». Pero ese es el camino hacia el caos. Imagina que cualquiera pudiera cambiar tu saldo, el nombre de usuario o incluso llamar a un método que debería ser solo de uso interno. En programas grandes, esto conduce a errores y bugs «mágicos».
Regla de oro para el desarrollador Java:
Primero hazlo todo private y luego expón solo lo que realmente haga falta.
2. Ámbito de las variables
El ámbito es la zona del código en la que una variable «existe» y puede usarse. Si sales de esa zona, la variable «desaparece», como si nunca hubiera existido.
En Java hay varios tipos de variables según su ámbito:
- Variables locales — declaradas dentro de un método o de un bloque de código { ... }.
- Parámetros de método — declarados entre paréntesis del método.
- Campos de clase — declarados dentro de la clase, pero fuera de los métodos.
Si una variable se declara dentro de { bloque },
solo es visible dentro de ese bloque
y no está disponible fuera de él.
Variables locales
Una variable local vive solo dentro del método o bloque en el que se declara.
void printSum(int a, int b)
{
int sum = a + b; // variable local
System.out.println(sum);
}
// aquí sum ya no existe
Un intento de acceder a sum fuera del método provocará un error de compilación: «No se puede encontrar la variable sum».
Parámetros de método
Los parámetros también son variables, pero solo viven dentro del método.
void greet(String name)
{
System.out.println("Hola, " + name);
}
// aquí name ya no existe
Campos de clase (miembros)
Los campos de clase se declaran dentro de la clase, pero fuera de los métodos. Son visibles en todos los métodos de esa clase.
public class Counter
{
private int count = 0; // campo de la clase
public void increment()
{
count++; // podemos usar el campo
}
public int getCount()
{
return count; // también podemos usar el campo
}
}
3. Sombreado de variables (shadowing)
El sombreado (shadowing) es la situación en la que, dentro de un ámbito, se declara una variable (o un parámetro) con el mismo nombre que en un ámbito externo. Dentro de ese bloque, el «nuevo» nombre sombrea al anterior y ya no se puede acceder directamente al valor externo.
Ejemplo de sombreado:
class ShadowDemo
{
int value = 10; // campo de la clase
void printValue()
{
System.out.println(value); // 10 — imprime el campo de la clase
int value = 5; // la variable local sombrea el campo de la clase
System.out.println(value); // imprime 5, no 10
}
}
En este ejemplo, cuando escribimos int value = 5;, declaramos una nueva variable local que tiene prioridad sobre el campo de clase con el mismo nombre. Al acceder a value dentro del método printValue(), se usará la variable local y no el campo de la clase.
Si aun así necesitas acceder a un campo estático de la clase, usa el nombre de la clase como prefijo:
class ShadowDemo
{
static int value = 10; // campo estático de la clase
void printValue()
{
System.out.println(value); // 10 — campo de la clase
int value = 5;
System.out.println(value); // 5 — variable local
System.out.println(ShadowDemo.value); // 10 — campo estático de la clase, acceso a través de 'ShadowDemo'
}
}
Si necesitas acceder a un campo no estático de la clase, se usa la palabra clave this. Indica la instancia actual del objeto.
class ShadowDemo
{
int value = 10;
void printValue()
{
System.out.println(value); // 10 — campo de la clase
int value = 5;
System.out.println(value); // 5 — variable local
System.out.println(this.value); // 10 — campo de la clase, acceso a través de 'this'
}
}
4. Práctica: modificadores de acceso y ámbito
Sigamos desarrollando la aplicación didáctica: un sistema sencillo de gestión de estudiantes. Pondremos campos y métodos con distintos modificadores de acceso.
Ejemplo: clase Student
public class Student
{
public String name; // nombre del estudiante (visible para todos)
private int age; // edad (visible solo dentro de la clase)
public Student(String name, int age)
{
this.name = name;
this.age = age;
}
public void sayHello()
{
System.out.println("Hola, me llamo " + name);
}
private void printSecret()
{
System.out.println("Mi edad: " + age);
}
public void revealSecret()
{
printSecret(); // se puede llamar a un método privado dentro de la clase
}
}
Uso de la clase Student
public class Main
{
public static void main(String[] args)
{
Student s = new Student("Vasya", 20);
s.sayHello(); // OK: método público
s.revealSecret(); // OK: método público que llama a uno privado por dentro
s.age = 30; // ¡Error! El campo age es private
s.printSecret(); // ¡Error! El método printSecret es private
}
}
5. Errores típicos al trabajar con modificadores de acceso y ámbito
Error n.º 1: ¡Todo public — y hola, caos!
Si haces públicos todos los campos y métodos, corres el riesgo de perder el control sobre quién y cómo cambia tus datos. Haz los campos private y expón solo lo necesario.
Error n.º 2: Intentar usar una variable fuera de su ámbito.
Por ejemplo, declaras una variable dentro de un método y luego intentas acceder a ella desde fuera: obtendrás un error de compilación. Las variables locales viven solo en su bloque { ... }.
Error n.º 3: Conflicto de nombres (sombreado de variables).
Si declaras en un método una variable con el mismo nombre que un campo de la clase, es fácil confundirse sobre qué variable estás usando. Usa this para señalar explícitamente el campo de la clase: this.value.
Error n.º 4: Intentar acceder a un método o campo private desde otra clase.
Si un método o campo está marcado como private, solo es accesible dentro de la misma clase. Intentar acceder desde fuera provocará un error de compilación.
Error n.º 5: Olvidar el ámbito en un bucle o bloque.
Una variable declarada dentro de un bucle o cualquier bloque { } vive solo dentro de ese bloque. Fuera de él no existe.
GO TO FULL VERSION