1. Overview
1.概述
In this quick tutorial, we’ll discuss how can we access the value of a private field from a different class in Java.
在这个快速教程中,我们将讨论如何从Java的不同类中访问private字段的值。
Before starting with the tutorial, we need to understand that the private access modifier prevents the accidental misuse of fields. However, if we wish to access them, we can do so by using the Reflection API.
在开始学习本教程之前,我们需要了解,private访问修饰符可以防止字段的意外误用。然而,如果我们希望访问它们,我们可以通过使用反射API来实现。
2. Example
2.例子
Let’s define a sample class Person with some private fields:
让我们定义一个带有一些private字段的示例类Person。
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. Making private Fields Accessible
3.让私人领域可以使用
To make any private field accessible, we have to call the Field#setAccessible method:
要使任何private字段可被访问,我们必须调用Field#setAccessible方法:。
Person person = new Person();
Field nameField = person.getClass().getDeclaredField("name");
nameField.setAccessible(true);
In the above example, we first specify the field that we want to retrieve – name – by using the Class#getDeclaredField method. Then we make the field accessible using nameField.setAccessible(true).
在上面的例子中,我们首先通过使用Class#getDeclaredField方法指定我们要检索的字段–name。然后我们使用nameField.setAccessible(true)使该字段可被访问。
4. Accessing private Primitive Fields
4.访问private原始字段
We can access the private fields that are primitives by using the Field#getXxx methods.
我们可以通过使用Field#getXxx方法访问属于基元的私有字段。
4.1. Accessing Integer Fields
4.1.访问整数字段
We can use the getByte, getShort, getInt, and getLong methods to access the byte, short, int, and long fields, respectively:
我们可以使用getByte, getShort, getInt, 和getLong方法来分别访问byte, short, int, 和long字段。
@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);
}
It’s also possible to perform autoboxing with primitive types:
也可以对原始类型执行autoboxing:。
@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);
}
The getXxx methods for primitive data types also support widening:
原始数据类型的getXxx方法也支持widening:。
@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. Accessing Floating Type Fields
4.2.访问浮动类型字段
To access float and double fields, we need to use the getFloat and getDouble methods, respectively:
要访问float和double字段,我们需要分别使用getFloat和getDouble方法。
@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. Accessing Character Fields
4.3.访问字符字段
To access the char fields, we can use the getChar method:
为了访问char字段,我们可以使用getChar方法。
@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. Accessing Boolean Fields
4.4.访问布尔字段
Similarly, we can use the getBoolean method to access the boolean field:
同样地,我们可以使用getBoolean方法来访问boolean字段。
@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. Accessing private Fields That Are Objects
5.访问属于对象的private字段
We can access the private fields that are objects by using the Field#get method. It is to note that the generic get method returns an Object, so we’ll need to cast it to the target type to make use of the value:
我们可以通过使用Field#get方法访问属于对象的私有字段。需要注意的是,通用的get方法会返回一个Object,所以我们需要将其转换为目标类型来使用该值。
@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. Exceptions
6.例外情况
Now, let’s discuss the exceptions that the JVM can throw while accessing the private fields.
现在,让我们讨论一下JVM在访问private字段时可能抛出的异常。
6.1. IllegalArgumentException
6.1.IllegalArgumentException
The JVM will throw IllegalArgumentException if we use a getXxx accessor that is incompatible with the target field’s type. In our example, if we write nameField.getInt(person), the JVM throws this exception since the field is of type String and not int or Integer:
JVM将抛出IllegalArgumentException 如果我们使用的getXxx访问器与目标字段的类型不兼容。在我们的例子中,如果我们写nameField.getInt(person),JVM会抛出这个异常,因为字段的类型是String而不是int或Integer。
@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));
}
As we’ve already seen, the getXxx methods support widening for the primitive types. It’s important to note that we need to provide the correct target for widening to be successful. Otherwise, the JVM throws an IllegalArgumentException:
正如我们已经看到的,getXxx方法支持对原始类型的拓宽。需要注意的是,我们需要提供正确的目标,才能成功拓宽。否则,JVM会抛出一个IllegalArgumentException。
@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
6.2.IllegalAccessException
The JVM will throw an IllegalAccessException if we’re trying to access a field that doesn’t have access rights. In the above example, if we don’t write the statement nameField.setAccessible(true), then the JVM throws the exception:
JVM将抛出一个IllegalAccessException 如果我们试图访问一个没有访问权限的字段。在上面的例子中,如果我们不写语句nameField.setAccessible(true),那么JVM会抛出这个异常。
@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
6.3.NoSuchFieldException
If we try to access a field that does not exist in the Person class, then the JVM could throw NoSuchFieldException:
如果我们试图访问一个在Person类中不存在的字段,那么JVM可能会抛出NoSuchFieldException。
Assertions.assertThrows(NoSuchFieldException.class,
() -> person.getClass().getDeclaredField("firstName"));
6.4. NullPointerException
6.4.NullPointerException
Finally, as you’d expect, the JVM throws a NullPointerException if we pass the field name as null:
最后,正如你所期望的,JVM会抛出一个NullPointerException 如果我们把字段名传成null。
Assertions.assertThrows(NullPointerException.class,
() -> person.getClass().getDeclaredField(null));
7. Conclusion
7.结语
In this tutorial, we’ve seen how we can access the private fields of a class in another class. We’ve also seen the exceptions that the JVM can throw and what causes them.
在本教程中,我们已经看到了如何在另一个类中访问一个类的private字段。我们还看到了JVM可以抛出的异常以及导致这些异常的原因。
As always, the complete code for this example is available over on GitHub.
一如既往,本例的完整代码可在GitHub上获得over。