在Java继承体系中,父类定义的私有属性会被子类继承,但子类无法直接访问这些私有属性。当我们需要在子类中重写toString()方法,完整输出对象包含父类私有属性在内的所有信息时,就需要采用特定的处理策略。下面先介绍第一种常用的处理方式。

策略一:父类提供公共访问方法
这是最符合Java封装设计原则的处理方式,核心思路是在父类中为私有属性提供公共的getter方法,子类在重写toString()方法时,通过调用父类的getter方法获取私有属性的值,再拼接到输出字符串中。
实现步骤
- 在父类中为需要输出的私有属性定义公共的getter方法
- 子类重写toString()方法,调用父类getter方法获取私有属性值
- 将父类属性和子类自有属性拼接成完整的输出字符串
代码示例
首先定义父类Person,包含私有属性name和age:
public class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
// 为私有属性提供公共getter方法
public String getName() {
return name;
}
public int getAge() {
return age;
}
}
然后定义子类Student,继承Person并添加自有属性studentId:
public class Student extends Person {
private String studentId;
public Student(String name, int age, String studentId) {
super(name, age);
this.studentId = studentId;
}
@Override
public String toString() {
// 调用父类getter方法获取私有属性值
return "Student{" +
"name='" + getName() + ''' +
", age=" + getAge() +
", studentId='" + studentId + ''' +
'}';
}
}
测试代码:
public class Test {
public static void main(String[] args) {
Student student = new Student("张三", 20, "2023001");
System.out.println(student.toString());
}
}
运行后输出结果为:Student{name='张三', age=20, studentId='2023001'},可以看到父类的私有属性也被完整输出了。
策略二:利用反射机制获取私有属性
如果无法修改父类的代码,没有可用的getter方法,就可以使用反射机制来访问父类的私有属性。反射可以打破Java的访问控制限制,获取对象的私有字段值,但这种方式会破坏封装性,且性能略低,一般作为备选方案使用。
实现步骤
- 获取子类的Class对象,再通过getSuperclass()方法获取父类Class对象
- 调用getDeclaredField()方法获取父类的私有字段
- 设置字段的可访问性为true,解除访问限制
- 通过get()方法获取字段对应的值
代码示例
假设父类Person没有提供getter方法,代码如下:
public class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
}
子类Student同样没有修改父类权限,重写toString()方法时使用反射获取父类私有属性:
import java.lang.reflect.Field;
public class Student extends Person {
private String studentId;
public Student(String name, int age, String studentId) {
super(name, age);
this.studentId = studentId;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder("Student{");
try {
// 获取父类Class对象
Class<? super Student> superClass = getClass().getSuperclass();
// 获取父类私有字段name
Field nameField = superClass.getDeclaredField("name");
// 设置可访问
nameField.setAccessible(true);
String name = (String) nameField.get(this);
sb.append("name='").append(name).append(''');
// 获取父类私有字段age
Field ageField = superClass.getDeclaredField("age");
ageField.setAccessible(true);
int age = (int) ageField.get(this);
sb.append(", age=").append(age);
} catch (Exception e) {
e.printStackTrace();
}
sb.append(", studentId='").append(studentId).append(''');
sb.append('}');
return sb.toString();
}
}
测试代码和之前一致,运行后同样可以输出包含父类私有属性的完整信息。需要注意的是,反射操作可能会抛出NoSuchFieldException、IllegalAccessException等异常,需要做好异常处理。
两种策略的对比
我们可以通过表格对比两种策略的特点,方便根据实际场景选择:
| 对比维度 | 父类提供公共访问方法 | 反射机制获取 |
|---|---|---|
| 封装性 | 符合封装原则,不破坏访问控制 | 打破封装,直接访问私有属性 |
| 代码可维护性 | 代码清晰,可读性强,后续修改方便 | 代码复杂,依赖字段名称,父类字段修改易出错 |
| 性能 | 无额外性能开销 | 反射操作有性能损耗 |
| 适用场景 | 可以修改父类代码的情况 | 无法修改父类代码的特殊情况 |
注意事项
- 优先选择父类提供公共访问方法的策略,符合Java面向对象的设计思想,减少后续维护成本
- 使用反射时需要注意字段名称的准确性,如果父类字段名修改,子类的反射代码也需要同步修改
- 如果父类私有属性是final类型,反射获取时不需要额外处理,正常获取即可
- 重写toString()方法时,建议输出所有对对象状态有影响的属性,方便调试和日志输出
在实际开发中,大部分情况下我们都可以通过修改父类代码添加getter方法的方式处理,只有在依赖第三方库等无法修改父类代码的场景下,才考虑使用反射方案。合理选择处理策略,既能满足功能需求,也能保证代码的规范性和可维护性。
Java继承toString方法父类私有属性方法重写访问控制修改时间:2026-07-08 06:18:27