在Java面向对象编程中,继承是实现代码复用和扩展的核心机制,子类会自动继承父类的非私有属性和方法,但在实际开发中,子类常常需要显式地访问父类的属性值,以完成特定的逻辑处理或功能扩展,本文将系统介绍Java中访问父类属性值的多种方法、适用场景及注意事项,帮助开发者更好地理解和运用继承特性。

直接访问:基于访问修饰符的权限控制
Java通过访问修饰符(public、protected、默认、private)控制类成员的可见性,子类对父类属性的访问权限直接受限于这些修饰符。
-
public属性:父类中用public修饰的属性,子类可以直接在任何位置访问,不受包限制。
class Parent { public String publicName = "Parent"; } class Child extends Parent { public void printName() { System.out.println(this.publicName); // 直接访问父类public属性 } }子类Child的方法中通过
this.publicName或直接publicName即可访问父类的public属性。 -
protected属性:protected属性允许同一包中的类及所有子类访问,子类即使与父类不在同一包,也能直接访问:
class Parent { protected String protectedName = "Parent"; } class Child extends Parent { public void printName() { System.out.println(this.protectedName); // 访问父类protected属性 } }protected属性通常用于子类需要“继承并扩展”的场景,既封装了内部实现,又允许子类访问。
-
默认(包私有)属性:若父类属性未加修饰符,仅允许同一包中的子类访问,若子类与父类在不同包,则无法直接访问:
package com.parent; class Parent { String defaultName = "Parent"; // 默认修饰符 } package com.child; import com.parent.Parent; class Child extends Parent { public void printName() { // System.out.println(this.defaultName); // 编译错误:不同包无法访问默认属性 } }默认属性适用于包内逻辑封装,跨包子类需通过其他方式访问。
-
private属性:private属性仅能在父类内部访问,子类无法直接访问,此时需通过父类的public或protected方法(如getter)间接访问:
class Parent { private String privateName = "Parent"; public String getName() { return this.privateName; } } class Child extends Parent { public void printName() { System.out.println(this.getName()); // 通过父类getter访问private属性 } }private属性是封装性的核心,子类通过父类提供的公共接口访问,既保证了数据安全,又实现了可控的访问。
super关键字:明确指向父类属性
当子类与父类存在同名属性时,子类会“隐藏”父类的属性(即子类属性覆盖父类属性),此时若需访问父类属性,必须使用super关键字明确指定。

访问被隐藏的父类属性
class Parent {
String name = "Parent";
}
class Child extends Parent {
String name = "Child"; // 隐藏父类name属性
public void printNames() {
System.out.println("Child name: " + this.name); // 访问子类name
System.out.println("Parent name: " + super.name); // 访问父类name
}
}
输出结果为:
Child name: Child
Parent name: Parent
super.name直接指向父类的name属性,避免了子类属性的干扰。
在子类构造器中访问父类属性
子类构造器默认会调用父类的无参构造器(super()),此时父类属性已完成初始化,子类构造器可通过super访问父类属性:
class Parent {
String name;
public Parent() {
this.name = "Parent";
}
}
class Child extends Parent {
String childName;
public Child() {
super(); // 默认调用父类无参构造,父类name已初始化
this.childName = super.name + " Child"; // 访问父类name
}
public void printNames() {
System.out.println("Parent name: " + super.name);
System.out.println("Child name: " + this.childName);
}
}
输出结果为:
Parent name: Parent
Child name: Parent Child
需注意:super只能在子类实例方法、构造器或初始化块中使用,且不能在静态上下文中调用(静态方法/块属于类级别,无父类实例概念)。
反射机制:突破访问限制的底层操作
当父类属性为private且未提供公共getter方法时,可通过Java反射机制强制访问,反射允许程序在运行时动态获取类的信息并操作其成员,包括私有属性。
访问父类的私有属性
import java.lang.reflect.Field;
class Parent {
private String privateName = "Parent";
}
class Child extends Parent {
public void accessPrivateName() throws Exception {
// 获取父类Class对象
Class<?> parentClass = Parent.class;
// 获取私有字段(需指定字段名)
Field privateField = parentClass.getDeclaredField("privateName");
// 设置可访问性(突破private限制)
privateField.setAccessible(true);
// 获取字段值(由于是父类属性,需通过子类实例或父类实例获取)
String value = (String) privateField.get(this);
System.out.println("Parent privateName: " + value);
}
}
调用Child的accessPrivateName()方法,输出:
Parent privateName: Parent
反射的核心步骤包括:获取Class对象、通过getDeclaredField()获取字段(包括私有字段)、调用setAccessible(true)解除访问限制、通过get()方法获取字段值。
反射的注意事项
- 安全性:反射会破坏封装性,可能导致代码可维护性降低,仅在特殊场景(如框架开发、序列化/反序列化)使用。
- 性能开销:反射操作比直接访问慢,需避免在性能敏感的代码中高频使用。
- 异常处理:反射可能抛出
NoSuchFieldException(字段不存在)、IllegalAccessException(非法访问)等异常,需妥善处理。
典型应用场景:继承、多态与封装的实践
继承中的属性扩展与复用
子类通过访问父类属性,实现功能的增量扩展,父类Animal有属性name和age,子类Dog新增属性breed,同时复用父类属性:
class Animal {
protected String name;
protected int age;
public Animal(String name, int age) {
this.name = name;
this.age = age;
}
}
class Dog extends Animal {
private String breed;
public Dog(String name, int age, String breed) {
super(name, age); // 调用父类构造器初始化父类属性
this.breed = breed;
}
public void showInfo() {
System.out.println("Name: " + super.name + ", Age: " + super.age + ", Breed: " + this.breed);
}
}
子类Dog通过super(name, age)访问父类构造器初始化属性,再通过super.name和super.age在方法中引用父类属性,实现属性复用与扩展。

多态中的属性访问规则
多态性中,父类引用指向子类对象时,属性访问遵循“编译时看声明类型,运行时看实际对象”的原则,但属性不存在重写,只有隐藏:
class Parent {
String name = "Parent";
}
class Child extends Parent {
String name = "Child";
}
public class Test {
public static void main(String[] args) {
Parent parent = new Parent();
Child child = new Child();
Parent polyRef = child; // 父类引用指向子类对象
System.out.println(parent.name); // 输出: Parent(声明类型Parent的属性)
System.out.println(polyRef.name); // 输出: Parent(声明类型Parent的属性,属性不重写)
}
}
若需通过多态引用访问子类隐藏的父类属性,需强制转换为子类类型后使用super:
System.out.println(((Child) polyRef).super.name); // 输出: Parent
注意事项:避免陷阱与最佳实践
-
优先遵循封装原则:即使能直接访问父类属性,也应优先通过父类的公共方法(getter/setter)操作,避免破坏封装,父类private属性应提供public getter,子类通过getter访问,而非直接或反射访问。
-
构造顺序与属性初始化:子类构造器默认调用父类无参构造,若父类未提供无参构造,需手动调用
super(...),父类属性在父类构造器中初始化,子类构造器中访问父类属性时,需确保父类构造已完成。 -
避免过度继承:多层继承可能导致属性访问路径复杂,增加维护成本,可考虑组合模式替代部分继承逻辑,减少对父类属性的依赖。
-
谨慎使用反射:反射仅用于无法通过常规方式访问的场景(如框架集成),业务代码中应避免直接操作私有属性,防止引发不可预期的副作用。
Java中访问父类属性值的方法多样,需根据场景选择合适的方式:直接访问适用于public/protected属性;super关键字用于解决子类属性隐藏问题;反射机制则突破了访问限制,但需谨慎使用,核心原则是:在满足功能需求的同时,遵循面向对象的封装性、可维护性设计,合理利用继承特性,实现代码的高效复用与扩展,通过理解不同方法的底层逻辑和适用场景,开发者可以更灵活地处理子类与父类之间的属性交互,构建健壮的面向对象系统。













