Clase con un campo privado

Todos conocen muy bien los modificadores de acceso a campos. Y si un campo tiene el modificador privado , entonces no podemos acceder a él desde el exterior.


public class Person {
  private int age;
  public String nickname;
  public Person(int age, String nickname) {
   this.age = age;
   this.nickname = nickname;
  }
}

Comprobemos la accesibilidad en nuestra clase principal :


public class Main {
   public static void main(String[] args) {
     Person person = new Person();  
     System.out.println(person.nickname);
    // System.out.println(person.age); No access to the field
  }
}

No tenemos acceso al age campo, pero tenemos reflexión. :) Y con su ayuda, podemos acceder y trabajar con campos privados.

Obteniendo un campo privado de un objeto usando la reflexión

Obtengamos una matriz de todos los campos de nuestra clase usando el método getDeclaredFields() . Devuelve un objeto Field con el que podemos trabajar y modificar:


public static void main(String[] args) {
        Field[] fields = Person.class.getDeclaredFields();
        List<String> actualFieldNames = getFieldNames(fields);
        actualFieldNames.forEach(System.out::println);
    }

    static List<String> getFieldNames(Field[] fields) {
        List<String> fieldNames = new ArrayList<>();
        for (Field field : fields)
            fieldNames.add(Modifier.toString(field.getModifiers()) + " " + field.getName());
        return fieldNames;
    }

apodo público de edad privada

En el método getFieldNames , obtenemos dos campos de nuestra clase. El método getModifiers devuelve el modificador de nuestro campo y getName devuelve su nombre. Ahora intentemos modificar y acceder a este campo. Primero, intentemos obtener datos de un campo público:


public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
    Person person = new Person(10, "CodeGym");

    Field field = Person.class.getDeclaredField("nickname");
    String nickname = (String) field.get(person);
    System.out.println(nickname);

    System.out.println(person.nickname);
}
CódigoGimnasio
CódigoGimnasio

Podemos acceder al campo tanto con la ayuda de la reflexión como usando nuestra referencia al objeto. ¡Todo esta bien! Pasemos a un campo privado.

Cambiaremos el nombre del campo solicitado a nuestro privado age campo:


public static void main(String[]args)throws NoSuchFieldException, IllegalAccessException {
		Person person = new Person(10, "CodeGym");

    Field field = Person.class.getDeclaredField("age");
    int age =(int)field.get(person);
    System.out.println(age);

    // System.out.println(person.age);
}

No tenemos acceso a través del objeto creado, así que intente usar la reflexión. Obtenemos un error:

Excepción de acceso ilegal

Obtenemos una IllegalAccessException . Echemos un vistazo a lo que hay dentro:

Intentemos resolverlo.

Se genera una excepción IllegalAccessException cuando una aplicación intenta crear una instancia de forma reflexiva (que no sea una matriz), establecer u obtener un campo, o invocar un método, cuando el método que se está ejecutando actualmente no tiene acceso a la definición de la clase, campo, método o constructor.

Los métodos aquí también lanzan esta excepción. Para evitar esta excepción, utilizaremos un método especial para acceder al campo privado.

setAccesible(bandera booleana)

Este método permite evitar controles de acceso de seguridad para un campo o clase. Podemos pasar verdadero o falso al método para determinar si se realizarán comprobaciones de acceso de seguridad para el campo. Intentemos arreglar nuestro código:


public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
    Person person = new Person(10, "CodeGym");

    Field field = Person.class.getDeclaredField("age");
    field.setAccessible(true);

    int age = (int) field.get(person);
    System.out.println("The current value is " + age);
}

Veamos el resultado:

El valor actual es 10

¡Excelente! Tenemos información sobre nuestra clase. Intentemos también cambiar nuestro campo asignándole un nuevo valor:


public static void main(String[]args)throws NoSuchFieldException, IllegalAccessException {
Person person = new Person(10, "CodeGym");

    Field field = Person.class.getDeclaredField("age");
    field.setAccessible(true);

    field.set(person, 19);

    int age =(int)field.get(person);
    System.out.println("The current value is " + age);
}

Después de cambiar nuestro campo, obtenemos:

El valor actual es 19

Intentemos llamar a setAccessible(false) .


public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
    Person person = new Person(10, "CodeGym");

    Field field = Person.class.getDeclaredField("age");
    
    field.setAccessible(true);
    field.set(person, 19);
    field.setAccessible(false);

    System.out.println("The current value is " + field.get(person));
}

Después de restaurar la accesibilidad a false , nos encontramos con nuestra excepción nuevamente cuando intentamos llamar al método get :

¡Así que tenga cuidado cuando trabaje con campos privados y no olvide que la reflexión es una herramienta muy poderosa!