帶有私有字段的類

你們都非常了解字段訪問修飾符。如果一個字段有private修飾符,那麼我們就不能從外部訪問它。


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

讓我們檢查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
  }
}

我們無權訪問年齡領域,但我們確實有反思。:) 在它的幫助下,我們可以訪問和使用私有字段。

使用反射從對像中獲取私有字段

讓我們使用getDeclaredFields()方法獲取我們類中所有字段的數組。它返回一個我們可以使用和修改的Field對象:


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;
    }
私人年齡
公開暱稱

getFieldNames方法中,我們從類中獲取兩個字段。getModifiers 方法返回字段的修飾符,getName 方法返回其名稱。現在讓我們嘗試修改和訪問這個字段。首先,讓我們嘗試從公共字段獲取數據:


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
代碼健身房

我們可以藉助反射和使用我們對對象的引用來訪問該字段。一切都很好!讓我們進入一個私人領域。

我們會將請求字段的名稱更改為我們的私有字段年齡場地:


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

我們無法通過創建的對象進行訪問,因此請嘗試使用反射。我們得到一個錯誤:

非法訪問異常

我們得到一個IllegalAccessException。讓我們來看看裡面有什麼:

讓我們試著弄明白。

IllegalAccessException當應用程序試圖反射性地創建實例(數組除外)、設置或獲取字段或調用方法時拋出,當前正在執行的方法無權訪問指定類、字段的定義,方法或構造函數。

這裡的方法也會拋出這個異常。為了避免這種異常,我們將使用一種特殊的方法來訪問私有字段。

setAccessible(布爾標誌)

此方法可以避免對字段或類進行安全訪問檢查。我們可以將truefalse傳遞給該方法,以確定是否對該字段執行安全訪問檢查。讓我們嘗試修復我們的代碼:


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

讓我們看看結果:

當前值為 10

偉大的!我們得到了關於我們班級的信息。讓我們也嘗試通過為它分配一個新值來更改我們的字段:


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

改變我們的領域後,我們得到:

當前值為 19

讓我們嘗試調用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));
}

將可訪問性恢復為false後,當我們嘗試調用get方法時再次遇到異常:

所以在處理私有字段時要小心,不要忘記反射是一個非常強大的工具!