Class with a private field

You all know very well about field access modifiers. And if a field has the private modifier, then we cannot access it from the outside.

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

Let's check accessibility in our Main class:

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

We don't have access to the age field, but we do have reflection. :) And with its help, we can access and work with private fields.

Getting a private field from an object using reflection

Let's get an array of all the fields in our class using the getDeclaredFields() method. It returns a Field object that we can work with and modify:

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;
    }
private age
public nickname

In the getFieldNames method, we get two fields from our class. The getModifiers method returns our field's modifier, and getName returns its name. Now let's try to modify and access this field. First, let's try to get data from a public field:

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

We can access the field both with the help of reflection and using our reference to the object. Everything is great! Let's move on to a private field.

We'll change the name of the requested field to our private age field:

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

We don't have access through the created object, so try using reflection. We get an error:

IllegalAccessException

We get an IllegalAccessException. Let's take a peek at what's inside:

Let's try to figure it out.

An IllegalAccessException is thrown when an application attempts to reflectively create an instance (other than an array), set or get a field, or invoke a method, when the currently executing method does not have access to the definition of the specified class, field, method, or constructor.

The methods here also throw this exception. To avoid this exception, we will use a special method to access the private field.

setAccessible(boolean flag)

This method makes it possible to avoid security access checks for a field or class. We can pass true or false to the method to determine whether security access checks will be performed for the field. Let's try to fix our code:

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

Let's look at the result:

The current value is 10

Great! We got information about our class. Let's also try changing our field by assigning a new value to it:

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

After changing our field, we get:

The current value is 19

Let's try calling 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));
}

After restoring the accessibility to false, we run into our exception again when we try to call the get method:

So be careful when working with private fields, and don't forget that reflection is a very powerful tool!