1. Übersicht
In diesem kurzen Tutorial werden wir diskutieren, wie wir von einer anderen Klasse in Java auf den Wert eines privaten Feldes zugreifen können .
Bevor wir mit dem Tutorial beginnen, müssen wir verstehen, dass der Modifikator für den privaten Zugriff den versehentlichen Missbrauch von Feldern verhindert. Wenn wir jedoch darauf zugreifen möchten, können wir dies mithilfe der Reflection-API tun.
2. Beispiel
Definieren wir eine Beispielklasse Person mit einigen privaten Feldern:
public class Person { private String name = "John"; private byte age = 30; private short uidNumber = 5555; private int pinCode = 452002; private long contactNumber = 123456789L; private float height = 6.1242f; private double weight = 75.2564; private char gender = 'M'; private boolean active = true; // getters and setters }
3. Private Felder zugänglich machen
Um ein privates Feld zugänglich zu machen, müssen wir die Methode Field # setAccessible aufrufen :
Person person = new Person(); Field nameField = person.getClass().getDeclaredField("name"); nameField.setAccessible(true);
Im obigen Beispiel geben wir zuerst das Feld an, das wir abrufen möchten - name - mithilfe der Methode Class # getDeclaredField . Dann machen wir das Feld mit nameField.setAccessible (true) zugänglich .
4. Zugriff auf private primitive Felder
Mit den Field # getXxx- Methoden können wir auf die privaten Felder zugreifen, die Grundelemente sind.
4.1. Zugriff auf ganzzahlige Felder
Wir können die Methoden getByte, getShort , getInt und getLong verwenden, um auf die Felder byte , short , int und long zuzugreifen :
@Test public void whenGetIntegerFields_thenSuccess() throws Exception { Person person = new Person(); Field ageField = person.getClass().getDeclaredField("age"); ageField.setAccessible(true); byte age = ageField.getByte(person); Assertions.assertEquals(30, age); Field uidNumberField = person.getClass().getDeclaredField("uidNumber"); uidNumberField.setAccessible(true); short uidNumber = uidNumberField.getShort(person); Assertions.assertEquals(5555, uidNumber); Field pinCodeField = person.getClass().getDeclaredField("pinCode"); pinCodeField.setAccessible(true); int pinCode = pinCodeField.getInt(person); Assertions.assertEquals(452002, pinCode); Field contactNumberField = person.getClass().getDeclaredField("contactNumber"); contactNumberField.setAccessible(true); long contactNumber = contactNumberField.getLong(person); Assertions.assertEquals(123456789L, contactNumber); }
Es ist auch möglich, Autoboxing mit primitiven Typen durchzuführen:
@Test public void whenDoAutoboxing_thenSuccess() throws Exception { Person person = new Person(); Field pinCodeField = person.getClass().getDeclaredField("pinCode"); pinCodeField.setAccessible(true); Integer pinCode = pinCodeField.getInt(person); Assertions.assertEquals(452002, pinCode); }
Die getXxx- Methoden für primitive Datentypen unterstützen auch die Erweiterung:
@Test public void whenDoWidening_thenSuccess() throws Exception { Person person = new Person(); Field pinCodeField = person.getClass().getDeclaredField("pinCode"); pinCodeField.setAccessible(true); Long pinCode = pinCodeField.getLong(person); Assertions.assertEquals(452002L, pinCode); }
4.2. Zugriff auf Floating Type-Felder
Um den Zugriff Schwimmer und Doppel Felder, müssen wir die verwenden getFloat und getDouble Methoden dargestellt:
@Test public void whenGetFloatingTypeFields_thenSuccess() throws Exception { Person person = new Person(); Field heightField = person.getClass().getDeclaredField("height"); heightField.setAccessible(true); float height = heightField.getFloat(person); Assertions.assertEquals(6.1242f, height); Field weightField = person.getClass().getDeclaredField("weight"); weightField.setAccessible(true); double weight = weightField.getDouble(person); Assertions.assertEquals(75.2564, weight); }
4.3. Zugriff auf Zeichenfelder
Um auf die Zeichenfelder zuzugreifen , können wir die Methode getChar verwenden:
@Test public void whenGetCharacterFields_thenSuccess() throws Exception { Person person = new Person(); Field genderField = person.getClass().getDeclaredField("gender"); genderField.setAccessible(true); char gender = genderField.getChar(person); Assertions.assertEquals('M', gender); }
4.4. Zugriff auf Boolesche Felder
Ebenso können wir die getBoolean- Methode verwenden, um auf das boolesche Feld zuzugreifen :
@Test public void whenGetBooleanFields_thenSuccess() throws Exception { Person person = new Person(); Field activeField = person.getClass().getDeclaredField("active"); activeField.setAccessible(true); boolean active = activeField.getBoolean(person); Assertions.assertTrue(active); }
5. Zugriff auf private Felder, die Objekte sind
Mit der Field # get- Methode können wir auf die privaten Felder zugreifen , die Objekte sind . Es ist zu beachten, dass die generische get- Methode ein Objekt zurückgibt , daher müssen wir es in den Zieltyp umwandeln, um den Wert zu verwenden :
@Test public void whenGetObjectFields_thenSuccess() throws Exception { Person person = new Person(); Field nameField = person.getClass().getDeclaredField("name"); nameField.setAccessible(true); String name = (String) nameField.get(person); Assertions.assertEquals("John", name); }
6. Ausnahmen
Lassen Sie uns nun die Ausnahmen diskutieren, die die JVM beim Zugriff auf die privaten Felder auslösen kann .
6.1. IllegalArgumentException
Die JVM löst eine IllegalArgumentException aus , wenn wir einen getXxx- Accessor verwenden, der nicht mit dem Typ des Zielfelds kompatibel ist . Wenn wir in unserem Beispiel nameField.getInt (person) schreiben, löst die JVM diese Ausnahme aus, da das Feld vom Typ String und nicht vom Typ int oder Integer ist :
@Test public void givenInt_whenSetStringField_thenIllegalArgumentException() throws Exception { Person person = new Person(); Field nameField = person.getClass().getDeclaredField("name"); nameField.setAccessible(true); Assertions.assertThrows(IllegalArgumentException.class, () -> nameField.getInt(person)); }
Wie wir bereits gesehen haben, unterstützen die getXxx- Methoden die Erweiterung für die primitiven Typen. Es ist wichtig zu beachten, dass wir das richtige Ziel für die Erweiterung angeben müssen, um erfolgreich zu sein . Andernfalls löst die JVM eine IllegalArgumentException aus :
@Test public void givenInt_whenGetLongField_thenIllegalArgumentException() throws Exception { Person person = new Person(); Field contactNumberField = person.getClass().getDeclaredField("contactNumber"); contactNumberField.setAccessible(true); Assertions.assertThrows(IllegalArgumentException.class, () -> contactNumberField.getInt(person)); }
6.2. IllegalAccessException
Die JVM löst eine IllegalAccessException aus, wenn wir versuchen, auf ein Feld zuzugreifen, das keine Zugriffsrechte hat . Wenn wir im obigen Beispiel die Anweisung nameField.setAccessible (true) nicht schreiben, löst die JVM die Ausnahme aus:
@Test public void whenFieldNotSetAccessible_thenIllegalAccessException() throws Exception { Person person = new Person(); Field nameField = person.getClass().getDeclaredField("name"); Assertions.assertThrows(IllegalAccessException.class, () -> nameField.get(person)); }
6.3. NoSuchFieldException
Wenn wir versuchen, auf ein Feld zuzugreifen, das in der Person- Klasse nicht vorhanden ist , kann die JVM NoSuchFieldException auslösen :
Assertions.assertThrows(NoSuchFieldException.class, () -> person.getClass().getDeclaredField("firstName"));
6.4. NullPointerException
Schließlich löst die JVM erwartungsgemäß eine NullPointerException aus, wenn wir den Feldnamen als null übergeben :
Assertions.assertThrows(NullPointerException.class, () -> person.getClass().getDeclaredField(null));
7. Fazit
In diesem Tutorial haben wir gesehen, wie wir auf die privaten Felder einer Klasse in einer anderen Klasse zugreifen können . Wir haben auch die Ausnahmen gesehen, die die JVM auslösen kann, und was sie verursacht.
Wie immer ist der vollständige Code für dieses Beispiel auf GitHub verfügbar.