java的:
super 就是一把“强制改道”的钥匙——让方法调用跳过本类和任何可能的子类重写,直接从你指定的父类(或更高祖先)开始查找并执行。
把前面所有例子收拢成 3 点:
Parent p = new GrandChild(); // 运行时类是 GrandChild
GrandChild → Child → Parent → Object
这条继承链 自底向上 搜,第一个匹配的就会被调用。
所以:
• 如果 GrandChild 把方法重写了,就用 GrandChild 的版本;
• 如果没重写,就继续往上找 Child 或 Parent 的版本。
这就是“从对象真正的运行时类开始找方法”的意思——永远以“new 出的那一份对象”所属类为起点,而不是以变量声明的父类为起点。
把这句话拆成两步就秒懂:
class Parent { void foo(){ System.out.println("P"); } }
class Child extends Parent {
——void foo(){ System.out.println("C"); }
——void bar(){ super.foo(); } // 编译期就锁定:从 Parent 开始找 foo
}
无论将来再写 class GrandChild extends Child,只要调用 new GrandChild().bar(),
super.foo() 依旧 铁定 执行 Parent.foo(),不会因为 GrandChild 也重写了 foo() 而改变。
把“对象本身”想象成一块固定的内存区域(里面装着所有字段和方法表指针)。
无论你怎么转型、怎么调用,这块内存都不会被复制、移动或替换成别的对象。
举例:
class Parent {}
class Child extends Parent {}
Child c = new Child(); // c 指向 0x1234
Parent p = c; // p 也指向 0x1234
• c 和 p 只是两个不同名字的箭头,箭头指向的仍然是同一块内存(0x1234)。
• 调用 c.method() 或 p.method(),甚至 super.method(),传入的 this 始终是 0x1234 这一份对象。
对象(this/self)本身不会变,变的只是“从哪个类开始找方法”以及“编译器允许你看见哪些成员”。
向上转型在内存里只产生“一份”子类对象;父类引用只是同一个对象地址的另一种“查看方式”,没有额外分配父类实例。
────────────────
以 Java 64 位压缩指针为例,内存布局如下(只有一个对象):
低地址
┌----------------------┐
│ mark word (8 byte) │ 对象头
│ klass pointer (4) │
├----------------------┤
│ Parent 字段 x (4) │ ← 父类部分
│ Parent 字段 y (4) │
├----------------------┤
│ Child 字段 z (4) │ ← 子类扩展部分
└----------------------┘
高地址
• new Child() 一次性在堆里开辟整块空间,包含父类字段 + 子类字段。
• 变量 Parent p = new Child(); 只是在栈(或寄存器)里存了一个指针,指向上面那块内存;
这个指针的类型信息(静态类型 Parent)只在编译器/校验器阶段用来限制可见成员,运行时 JVM 仍按实际 klass 指针(Child 类)做动态派发。
因此:
• 没有额外的父类对象生成。
• 没有内存复制。
• 所谓的“向上转型”纯粹是引用层面的视角转换,对象实体始终只有一份。
因此 super 的唯一使命就是:
在需要父类原始实现时,强行避开动态派发的重写链,精准调用祖先版本。
下面把 Python 的 super 单独拎出来,告诉你它到底干了啥。
────────────────
一句话区别
• Java 的 super:编译期就写死“从父类开始找方法”。
• Python 的 super:运行时才动态生成一个 代理对象,帮你沿着 方法解析顺序(MRO) 去找下一个实现。
Python 的对象模型
Python 里所有类都有统一的 C 结构体(PyObject + PyTypeObject),
• 实例字典:每个实例有一个 dict 指针,指向 PyDictObject,里面就是 {属性名: 值}。
• 类字典:类对象(PyTypeObject)里还有一个 tp_dict,存放方法、类变量。
• 查找顺序:实例 dict → 类 dict → 父类 dict(MRO 链)。
• 因为都是哈希表,所以:

obj.new_attr = 123 # 运行时直接写实例字典
del obj.new_attr # 立刻删掉
但它没有 Java 那种“父类子类字段连续排放”的固定内存布局;
字段和方法都挂在 字典 里,运行时随便增删。
句话总结
Python 把所有数据和方法都挂到字典里,通过 PyObject → PyTypeObject → dict 的链条实现“运行时随便加属性/方法”。
这与 Java 的“字段在对象内存里连续排放 + 静态偏移”形成鲜明对比,也正是 Python 灵活但内存、性能开销更大的根源。
是的,前面一大段都是在讲 Java(字节码、invokespecial、编译期锁定父类……)。
下面把 Python 的 super 单独拎出来,告诉你它到底干了啥。
────────────────
一句话区别
• Java 的 super:编译期就写死“从父类开始找方法”。
• Python 的 super:运行时才动态生成一个 代理对象,帮你沿着 方法解析顺序(MRO) 去找下一个实现。
────────────────
当你写:
class Child(Parent):
def foo(self):
super().foo()
解释器会:
class Child(Parent):
def foo(self):
print("Child ", id(self))
super().foo() # 代理对象 -> 找 MRO 里的下一个类
c = Child()
c.foo()
输出(地址相同):
Child 140397714434240
Parent 140397714434240
────────────────
4. 小结对照表
维度 Java super Python super
决定时机 编译期锁定父类 运行时查 MRO
底层形式 字节码 invokespecial
运行期 superobject
代理
对象是否变化 否,this 不变 否,self 不变
查找起点 写死的父类 MRO 中“下一个”类
所以:
• 前面大段讲的是 Java;
• Python 的 super 则是 运行期按 MRO 找下一个实现,概念类似,实现完全不同。
typedef struct {
PyObject_HEAD
PyTypeObject *type; // 当前类(写 super() 时所在的类)
PyObject *obj; // 实例本身(self)
int obj_is_type; // 标记 obj 是不是类
} superobject;
────────────────
class Child(Parent):
def foo(self):
super().foo()
解释器会:
class Child(Parent):
def foo(self):
print("Child ", id(self))
super().foo() # 代理对象 -> 找 MRO 里的下一个类
c = Child()
c.foo()
Child 140397714434240
Parent 140397714434240
地址相同