Classe com um campo privado

Todos vocês sabem muito bem sobre modificadores de acesso de campo. E se um campo tiver o modificador privado , não podemos acessá-lo de fora.


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

Vamos verificar a acessibilidade em nossa classe Main :


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
  }
}

Não temos acesso aoidadecampo, mas temos reflexão. :) E com sua ajuda podemos acessar e trabalhar com campos privados.

Obtendo um campo privado de um objeto usando reflexão

Vamos obter um array de todos os campos da nossa classe usando o método getDeclaredFields() . Ele retorna um objeto Field com o qual podemos trabalhar e 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;
    }
idade privada
apelido público

No método getFieldNames , obtemos dois campos de nossa classe. O método getModifiers retorna o modificador do nosso campo e getName retorna seu nome. Agora vamos tentar modificar e acessar este campo. Primeiro, vamos tentar obter dados de um 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);
}
CodeGym
CodeGym

Podemos acessar o campo tanto com a ajuda da reflexão quanto usando nossa referência ao objeto. Tudo é bom! Vamos passar para um campo privado.

Mudaremos o nome do campo solicitado para nosso privadoidadecampo:


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);
}

Não temos acesso através do objeto criado, então tente usar a reflexão. Obtemos um erro:

IllegalAccessException

Obtemos um IllegalAccessException . Vamos dar uma olhada no que tem dentro:

Vamos tentar descobrir.

Uma IllegalAccessException é lançada quando um aplicativo tenta criar reflexivamente uma instância (diferente de uma matriz), definir ou obter um campo ou invocar um método, quando o método atualmente em execução não tem acesso à definição da classe, campo, método ou construtor.

Os métodos aqui também lançam essa exceção. Para evitar essa exceção, usaremos um método especial para acessar o campo privado.

setAccessible(sinalizador booleano)

Este método permite evitar verificações de acesso de segurança para um campo ou classe. Podemos passar true ou false para o método para determinar se as verificações de acesso de segurança serão executadas para o campo. Vamos tentar corrigir nosso 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);
}

Vejamos o resultado:

O valor atual é 10

Ótimo! Recebemos informações sobre nossa turma. Vamos também tentar alterar nosso campo atribuindo um novo valor a ele:


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);
}

Depois de mudar nosso campo, obtemos:

O valor atual é 19

Vamos tentar chamar 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));
}

Depois de restaurar a acessibilidade para false , encontramos nossa exceção novamente quando tentamos chamar o método get :

Portanto, tenha cuidado ao trabalhar com campos privados e não se esqueça de que a reflexão é uma ferramenta muito poderosa!