super()必须是子类构造器第一行,否则编译报错;未显式调用时编译器自动插入无参super(),但父类须有无参构造器;super.方法仅调直接父类非private方法,不支持跳过父类调祖父类;字段遮蔽时super.字段名是安全访问父类字段的唯一方式。

super() 必须是子类构造器第一行
Java 要求子类构造器中,如果显式调用 super() 或 this(),它必须是第一条语句。否则编译直接报错:call to super must be first statement in constructor。
常见错误是想在 if 判断后才决定调用哪个父类构造器——不行,super() 不能出现在条件分支里;也不能在它前面加日志、变量声明或任何其他代码。
- 正确写法:
super(name, age)紧贴左大括号后,无前置语句 - 错误写法:
System.out.println("init"); super(name);→ 编译失败 - 没写
super()?编译器会自动补super()(无参),但前提是父类有无参构造器;否则报错:constructor X in class Y cannot be applied to given types
super.方法名() 不是万能的“向上找”
用 super.methodName() 只能调用父类**直接定义**的方法,不能跳过父类去调祖父类;而且该方法不能是 private(私有方法不可见),也不能被子类重写后又想绕过它去调更上层的版本(Java 不支持类似 Python 的 super(A, self).method() 那种指定祖先类的语法)。
典型误用场景:父类 A 有 log(),子类 B 重写了它,B 的子类 C 想调 A 版本的 log() —— 这时 super.log() 实际调的是 B 的重写版,不是 A 的原始版。
立即学习“Java免费学习笔记(深入)”;
- 若真需要访问祖父类逻辑,通常得在父类 B 中暴露一个
protected final void logBase()供 C 调用 -
super.toString()在重写toString()时很常用,但要注意:如果父类实现返回 null 或空字符串,可能引发 NPE 或不符合预期 - 静态方法不能用
super.staticMethod()调用,只能用ParentClass.staticMethod();用super调静态方法不会报错,但实际走的是编译期绑定,和super无关
子类字段遮蔽父类字段时,super.字段名 是唯一安全引用方式
当子类定义了和父类同名的字段(比如父类有 protected String id,子类也写 private String id),就会发生字段遮蔽(field hiding)。此时直接用 id 访问的是子类字段,而 super.id 才能明确拿到父类那个。
这和方法重写不同:字段没有动态绑定,只有遮蔽;所以不显式写 super. 很容易拿错值,尤其在构造器或 setter 中混用时容易出 bug。
- 构造器里初始化父类字段,别写
this.id = id(可能指子类字段),应写super.id = id - getter 中返回
super.id还是this.id,取决于你到底要暴露哪一层的数据 - IDE 通常不会警告字段遮蔽,但编译通过不代表逻辑正确——运行时数据错位很难排查
super 在匿名内部类和 Lambda 中不可用
在匿名内部类里,super 指向的是该匿名类的直接父类(通常是某个接口或抽象类),不是外部方法所在类的父类;而在 Lambda 表达式中,根本不能出现 super 关键字,编译直接拒绝。
这意味着:如果你在匿名类里想调用外部类的父类方法,不能靠 super,得把外部类实例传进来,再通过它间接调用(比如 OuterClass.this.method(),再由 OuterClass 去调自己的 super.method())。
- 匿名类中写
super.toString(),调的是Object的(因为匿名类默认继承 Object),不是外部类父类的 - Lambda 中连
this都指向外部类实例,super完全不合法,语法层面禁止 - 这种限制不是疏漏,而是设计使然:Lambda 是函数式抽象,不建立新的类层次关系
真正麻烦的地方不在语法能不能写,而在于字段遮蔽和匿名类中的 super 行为——它们不报错,但结果和直觉相反,调试时容易卡很久。








