super关键字
目录
一、super关键字介绍
1.super关键字的核心作用
2.super与this对比
3.关键规则与注意事项
4.常见使用场景
5.总结
二、c继承b,b继承a c怎么调用a的方法或属性
1.直接调用A的成员(当B未覆盖时)
2.间接调用A的成员(当B覆盖时)
示例 1:方法覆盖时的间接调用
示例 2:属性隐藏时的间接调用
示例 3:构造方法链式调用
3.关键规则与注意事项
4.总结
三、super内存图
1.结合图片与代码的分析:
2.this和super的使用有显著区别
3.this.getName()和this.name
相同点
不同点
1. 访问方式
2. 封装性
3. JVM层面的差异
JVM 内存示意图
关键点解析
1. 栈(Stack)
2. 堆(Heap)
3. 元空间(Metaspace)
4. this.name vs this.getName() 的底层操作
super 的作用
总结
四、super注意点
1.this 和 super 不能用于静态上下文(如静态方法或静态代码块)
错误示例解释
1. System.out.println(this);
2. System.out.println(super.name);
解决方案
1. 将方法改为实例方法(移除 static)
2. 通过对象实例访问成员(不推荐)
关键总结
附加说明
2.super什么时候不能省略
3.this可以单独输出,super不能单独输出。
4.super()语法的作用
5.super()的默认调用
一、super关键字介绍
在Java中,super
关键字用于显式访问父类的成员(属性、方法)或调用父类的构造方法。以下是对super
关键字的详细总结,与this
关键字对比,便于理解:
1.super
关键字的核心作用
(1).访问父类成员
当子类与父类有同名属性或方法时,使用super.成员名访问父类成员,避免歧义。
示例:
class Parent {
String name = "Parent";
}
class Child extends Parent {
String name = "Child";
void print() {
System.out.println(super.name); // 输出 Parent
System.out.println(name); // 输出 Child
}
}
(2).调用父类构造方法
子类构造方法中必须调用父类构造方法(显式或隐式),格式为super(参数)。
规则:
①super(参数)必须出现在构造方法第一行。
②若未显式调用,默认调用super()(父类无参构造方法)。
③若父类无无参构造方法,子类必须显式调用父类的有参构造方法。
2.super
与this
对比
特性 | super | this |
---|---|---|
指向对象 | 当前对象的父类部分 | 当前对象本身 |
单独使用 | ❌ 不可(如System.out.println(super) ) | ✅ 可(如System.out.println(this) ) |
访问成员 | super.成员名 (访问父类成员) | this.成员名 (访问当前对象成员) |
调用构造方法 | super(参数) (父类构造方法) | this(参数) (本类其他构造方法) |
静态上下文 | ❌ 不可在静态方法中使用 | ❌ 不可在静态方法中使用 |
3.关键规则与注意事项
(1).构造方法的链式调用
子类构造方法必须直接或间接调用父类构造方法,最终追溯到Object类的无参构造方法。
示例:
class Parent {
Parent(int a) { /* ... */ }
}
class Child extends Parent {
Child() {
super(1); // 必须显式调用父类有参构造方法
}
}
(2).父类无参构造方法的隐式调用
若子类构造方法未显式调用super(参数)或this(参数),编译器自动插入super()。
风险:若父类无无参构造方法,编译报错。
(3).super在方法重写中的应用
在子类中调用父类被重写的方法:
@Override
void method() {
super.method(); // 调用父类实现
// 子类新增逻辑
}
4.常见使用场景
(1).解决成员命名冲突
当子类与父类有同名属性/方法时,用super明确访问父类成员。
⑵.初始化父类状态
子类构造方法中通过super(参数)确保父类属性正确初始化。
⑶.扩展父类功能
在子类方法中复用父类方法逻辑(如先调用super.method(),再添加新功能)。
5.总结
⑴.super的本质:一种访问父类成员的语法机制,非对象引用。
⑵.核心规则:
●构造方法中必须优先初始化父类。
●同名成员需用super明确访问父类版本。
⑶.与this的区别:super关注父类,this关注当前对象。
通过合理使用super,可以确保继承体系的正确性和代码的清晰性,尤其在处理重名成员和构造方法链时至关重要。
二、c继承b,b继承a c怎么调用a的方法或属性
在Java中,若类C继承B,B继承A,C想要直接调用A的方法或属性,需通过B类间接暴露对A的访问。以下是具体实现方式及场景分析:
1.直接调用A的成员(当B未覆盖时)
如果父类B未重写A的方法或隐藏A的属性,C可直接通过super调用A的成员。
示例代码
class A {
String name = "A";
void print() {
System.out.println("A的print方法");
}
}
class B extends A {
// B未重写A的print(),也未隐藏name属性
}
class C extends B {
void accessA() {
System.out.println(super.name); // 直接访问A的name
super.print(); // 直接调用A的print()
}
}
测试类Main
public class Main {
public static void main(String[] args) {
C c = new C();
c.accessA();
}
}
运行结果:
2.间接调用A
的成员(当B
覆盖时)
如果B重写了A的方法或隐藏了A的属性,C无法直接通过super访问A的成员。此时需在B中显式暴露A的成员,供C调用。
子类 C
如何通过间接方式调用 A
的成员。
示例 1:方法覆盖时的间接调用
场景
①A 定义了方法 print()
②B 重写了 print()
③C 需要通过 B 间接调用 A 的 print()
代码实现
// 祖父类 A
class A {
// A 类的方法
public void print() {
System.out.println("A 的 print 方法");
}
}
// 父类 B 继承 A
class B extends A {
// B 重写了 A 的 print()
@Override
public void print() {
System.out.println("B 的 print 方法");
}
// 定义一个新方法,通过 super 调用 A 的 print()
public void callAPrint() {
super.print(); // 调用父类 A 的 print()
}
}
// 子类 C 继承 B
class C extends B {
// C 通过调用 B 的 callAPrint() 间接访问 A 的 print()
public void accessA() {
callAPrint(); // 调用 B 暴露的 callAPrint()
}
}
// 测试类
public class Test {
public static void main(String[] args) {
C c = new C();
c.accessA(); // 输出:A 的 print 方法
}
}
运行结果:
关键注释
⑴.B 类中的 callAPrint() 方法
public void callAPrint() {
super.print(); // 通过 super 调用直接父类 A 的 print()
}
B 重写了 A 的 print(),但通过 callAPrint() 方法暴露了 A 的原始逻辑。
⑵. C 类中的 accessA() 方法
public void accessA() {
callAPrint(); // 调用 B 提供的 callAPrint()
}
C 无法直接通过 super.super.print() 调用 A 的 print(),必须通过 B 的 callAPrint() 间接访问。
示例 2:属性隐藏时的间接调用
场景
①A 定义了属性 name
②B 隐藏了 A 的 name(同名属性)
③C 需要通过 B 间接访问 A 的 name
代码实现
// 祖父类 A
class A {
protected String name = "A"; // 父类属性(protected 允许子类访问)
}
// 父类 B 继承 A
class B extends A {
protected String name = "B"; // 隐藏 A 的 name
// 提供一个方法返回 A 的 name
public String getAName() {
return super.name; // 通过 super 访问 A 的 name
}
}
// 子类 C 继承 B
class C extends B {
public void accessA() {
System.out.println(getAName()); // 输出 A 的 name
System.out.println(super.name); // 输出 B 的 name
}
}
// 测试类
public class Test {
public static void main(String[] args) {
C c = new C();
c.accessA(); // 输出:A \n B
}
}
运行结果:
关键注释
⑴.B 类中的 getAName() 方法
public String getAName() {
return super.name; // 返回父类 A 的 name
}
B 隐藏了 A 的 name,但通过 getAName() 方法暴露了 A 的原始属性。
⑵.C 类中的 accessA() 方法
System.out.println(getAName()); // 通过 B 的方法获取 A 的 name
System.out.println(super.name); // 直接访问 B 的 name
C 通过 getAName() 间接访问 A 的 name,而 super.name 直接访问 B 的 name。
示例 3:构造方法链式调用
场景
①A 有一个有参构造方法
②B 和 C 需要通过构造方法链式初始化 A
代码实现
// 祖父类 A
class A {
private int value;
public A(int value) {
this.value = value;
System.out.println("A 的构造方法,value=" + value);
}
}
// 父类 B 继承 A
class B extends A {
public B(int value) {
super(value); // 调用 A 的构造方法
System.out.println("B 的构造方法");
}
}
// 子类 C 继承 B
class C extends B {
public C() {
super(100); // 调用 B 的构造方法,最终链式调用 A 的构造方法
System.out.println("C 的构造方法");
}
}
// 测试类
public class Test {
public static void main(String[] args) {
C c = new C();
}
}
运行结果:
关键注释
⑴.B 的构造方法
public B(int value) {
super(value); // 必须显式调用 A 的有参构造方法
}
若 A 没有无参构造方法,子类必须通过 super 显式调用其有参构造方法。
⑵.C 的构造方法
public C() {
super(100); // 调用 B 的构造方法,链式触发 A 的构造方法
}
构造方法调用必须逐级向上传递,最终初始化 A。
3.关键规则与注意事项
⑴.super的链式限制
Java语法中,super只能指向直接父类(如C的super指向B),无法跨级访问祖父类A的成员。若需访问A的成员,必须通过B间接实现。
⑵.构造方法的调用
若要在C的构造方法中调用A的构造方法,必须通过B的构造方法链式传递:
class A {
A(int value) { /* ... */ }
}
class B extends A {
B(int value) {
super(value); // 调用A的构造方法
}
}
class C extends B {
C() {
super(10); // 通过B调用A的构造方法
}
}
⑶.访问权限限制
A的成员需对子类可见(如protected或public),否则无法在B或C中访问。
4.总结
①若B未覆盖A的成员:C可直接通过super调用A的成员。
②若B覆盖了A的成员:需在B中定义方法(如getAName()、callAPrint()),通过super暴露A的成员,供C间接调用。
③无法跨级使用super.super:Java语法不支持super.super,必须通过中间类B实现多层访问
关键语法:
①方法覆盖:在 B 中定义新方法,通过 super.方法名() 调用 A 的方法。
②属性隐藏:在 B 中定义方法返回 super.属性名,暴露 A 的属性。
③构造方法:通过 super(参数) 链式调用父类构造方法。
三、super内存图
父类Person:
public class Person {
String name;
int age;
String email;
String address;
public Person() {
}
public Person(String name, int age, String email, String address) {
this.name = name;
this.age = age;
this.email = email;
this.address = address;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
}
子类Teacher:
public class Teacher extends Person {
Double sal; //特有属性:工资
public Teacher(){
}
public Teacher(Double sal) {
this.sal = sal;
}
public Teacher(String name, int age, String email, String address, Double sal) {
super(name, age, email, address);
this.sal = sal;
}
public Double getSal() {
return sal;
}
public void setSal(Double sal) {
this.sal = sal;
}
public void display(){
System.out.println(this.getName());
System.out.println(this.name);
System.out.println(this.getAge());
System.out.println(this.getEmail());
System.out.println(this.getAddress());
System.out.println(this.getSal());
}
}
测试类Test01:
public class Test01 {
public static void main(String[] args) {
//创建Teacher对象
Teacher t=new Teacher("张三",20,"zhangsan.com","北京朝阳",10000.0);
t.display();//t变量为局部变量
}
}
运行结果:
分析:
1.结合图片与代码的分析:
⑴.super的定位
根据图片说明,super代表当前对象中的父类型特征,是对象内存结构的一部分。例如:
● Teacher对象在堆内存中包含从Person继承的name、age、email、address属性,以及自身的sal属性。
● 通过super(name, age, email, address)调用父类构造方法,初始化父类属性(对应堆中父类部分)。
⑵. super的使用场景
①构造方法中显式调用父类构造器
public Teacher(String name, int age, ...) {
super(name, age, email, address); // 调用父类Person的构造方法
this.sal = sal;
}
● 此处super用于初始化父类属性,确保父类部分正确构造。
● 若未显式调用super(),JVM会默认调用父类无参构造方法(需确保父类存在无参构造方法)。
②实例方法中访问父类成员
● 若子类重写了父类方法,可通过super.方法名()调用父类实现(示例代码未体现,但逻辑上可行)。
● 若父类属性为protected或包级可见,可直接通过super.属性名访问(例如super.name)。
⑶. super的限制
● 无法在静态上下文中使用
图片指出super与对象实例绑定,而静态方法(如main)不依赖实例,因此super和this均不可用。
⑷.JVM内存模型关联
● 堆(Heap):存储Teacher对象,包含父类Person的属性和子类特有属性sal。
● 方法区(Metaspace):存储类的元数据(如Person.class和Teacher.class),super的调用逻辑由编译器和JVM根据类信息解析。
● 栈帧(VM Stack):display()方法栈帧中的this=0x12指向当前对象,通过this和super可访问对象内存中的父类部分。
2.this和super的使用有显著区别
public Teacher(String name, int age, String email, String address, double sal) {
this.name = name;
this.age = age;
this.email = email;
this.address = address;
this.sal = sal;
}
public Teacher(String name, int age, String email, String address, Double sal) {
super(name, age, email, address);
this.sal = sal;
}
这里this和super是否有区别?
在提供的两段构造方法中,this
和super
的使用有显著区别,且在JVM层面也有不同的行为。以下是具体分析:
⑴.代码逻辑的区别
第一段代码(直接通过this赋值)
public Teacher(String name, int age, String email, String address, double sal) {
this.name = name; // 直接操作父类属性(继承自Person)
this.age = age; // 同上
this.email = email; // 同上
this.address = address;// 同上
this.sal = sal; // 子类特有属性
}
行为:直接通过this对继承自父类的属性赋值,未调用父类构造方法。
前提条件:父类属性必须是子类可访问的(如默认可见或protected)。
风险:
● 如果父类构造方法中有初始化逻辑(如数据校验、资源分配),这些逻辑会被跳过。
● 若父类属性改为private,此代码将无法编译。
第二段代码(通过super调用父类构造方法)
public Teacher(String name, int age, String email, String address, Double sal) {
super(name, age, email, address); // 显式调用父类构造方法
this.sal = sal; // 子类特有属性
}
行为:通过super调用父类构造方法,确保父类属性通过父类逻辑初始化。
优点:
● 符合继承规范,父类初始化逻辑(如数据校验)会被执行。
● 即使父类属性是private,也能正确初始化。
⑵.JVM层面的区别
-
使用
super
时:-
JVM会先在堆中为父类部分分配内存,并调用父类构造方法(
Person
的构造方法)完成初始化。 -
父类属性由父类构造方法设置,子类构造方法仅处理子类特有属性。
-
对应内存结构:
-
堆中
Teacher
对象包含父类Person
的完整结构(name
,age
,email
,address
)和子类属性sal
。
-
-
-
直接使用
this
赋值时:-
JVM默认调用父类无参构造方法(
super()
),但未显式初始化父类属性。 -
子类构造方法直接操作父类属性(需父类属性可见)。
-
风险:若父类无无参构造方法,或父类构造方法有副作用(如计数器递增),会导致逻辑错
-
⑶. 关键区别总结
场景 | 使用this 直接赋值 | 使用super 调用父类构造方法 |
---|---|---|
父类初始化逻辑 | 跳过父类构造方法中的逻辑 | 执行父类构造方法中的逻辑 |
属性访问权限 | 要求父类属性对子类可见(默认/protected ) | 不依赖属性可见性,通过构造方法初始化 |
代码健壮性 | 低(可能绕过父类校验逻辑) | 高(符合面向对象设计原则) |
JVM行为 | 默认调用父类无参构造方法(super() ) | 显式调用指定父类构造方法 |
⑷. 示例验证
假设父类Person
的构造方法中有数据校验逻辑:
public Person(String name, int age, String email, String address) {
if (age < 0) throw new IllegalArgumentException("年龄不能为负数");
this.name = name;
this.age = age;
this.email = email;
this.address = address;
}
-
使用
super
的构造方法:若传入age = -1
,会触发异常。 -
直接使用
this
赋值的构造方法:不会触发异常,直接赋值age = -1
,导致数据不一致。
⑸.结论
-
优先使用
super
:确保父类初始化逻辑被执行,代码更健壮、可维护。 -
直接使用
this
赋值的场景:仅在父类无构造方法逻辑且属性可见时可用,但需谨慎。 -
JVM层面:
super
显式调用父类构造方法是规范做法,避免潜在初始化问题。
3.this.getName()和this.name
这里System.out.println(this.getName()); System.out.println(this.name);输出同样的张三,区别与不同。
在Java中,this.getName()
和this.name
都可以访问Person
类的name
字段,但两者在实现方式和设计理念上有显著区别。以下是它们的异同点及JVM层面的分析:
相同点
-
结果一致
两者的最终结果相同,都返回Person
类中name
字段的值。例如,在Teacher
类的display()
方法中,this.getName()
和this.name
都会输出“张三”。 -
访问同一字段
无论是通过方法调用还是直接访问,操作的均是Person
类中的name
字段。
不同点
1. 访问方式
-
this.getName()
调用的是Person
类的public
方法getName()
,属于间接访问。方法内部直接返回name
字段。 -
this.name
直接访问Person
类的name
字段,属于直接访问。此时name
的可见性是默认的包级私有(没有显式修饰符),因此同一包内的Teacher
类可以直接访问。
2. 封装性
-
this.getName()
符合面向对象的封装原则。通过方法访问字段,可以在方法内部添加逻辑(如验证、计算),隐藏实现细节。 -
this.name
破坏封装性,直接暴露字段的实现。如果未来需要修改字段的存储方式(例如将name
拆分为firstName
和lastName
),直接访问的代码将无法适应变化。
3. JVM层面的差异
-
this.getName()
-
涉及方法调用,JVM需要执行以下步骤:
-
在栈中创建新的栈帧(stack frame)。
-
执行方法体(直接返回
name
字段)。 -
销毁栈帧并返回结果。
-
-
简单方法(如
getName()
)可能被JVM内联优化(inlining),消除方法调用的开销。
-
-
this.name
-
直接访问堆内存中的字段,无需方法调用,理论上效率更高(但实际差异可忽略)。
-
JVM 内存示意图
// 内存结构简化表示:
+-----------------------+
| **栈 (Stack)** |
|-----------------------|
| → main方法栈帧 |
| - 局部变量 t -----------→ [堆中的Teacher对象]
| |
| → display方法栈帧 |
| - this引用 ------------→ [堆中的Teacher对象]
| - 操作数栈 (临时存储数据) |
| - 局部变量表 |
+-----------------------+
↓
+-----------------------+
| **堆 (Heap)** |
|-----------------------|
| Teacher对象实例 |
|-----------------------|
| Person部分: |
| - name = "张三" |
| - age = 20 |
| - email = "zhangsan.com"|
| - address = "北京朝阳" |
|-----------------------|
| Teacher特有部分: |
| - sal = 10000.0 |
+-----------------------+
↓
+-----------------------+
| **元空间 (Metaspace)** |
|-----------------------|
| Person类元数据: |
| - 字段: name, age, email, address|
| - 方法: getName(), setAge(), ...|
|-----------------------|
| Teacher类元数据: |
| - 字段: sal |
| - 方法: display(), getSal()|
| - 继承链: Person → Teacher|
+-----------------------+
关键点解析
1. 栈(Stack)
-
main
方法栈帧-
局部变量
t
存储的是堆中Teacher
对象的内存地址(引用)。
-
-
display
方法栈帧-
this
引用:指向当前对象(即堆中的Teacher
对象),用于访问实例字段和方法。 -
super
:隐式存在于方法调用中,指向父类(Person
)的字段和方法(未显式使用,但通过继承链访问)。
-
2. 堆(Heap)
-
对象实例结构
-
子类
Teacher
的实例包含父类Person
的所有字段(name
,age
,email
,address
)和自身特有字段sal
。 -
字段直接存储在堆中,
this.name
通过内存地址直接访问。
-
3. 元空间(Metaspace)
-
存储类元数据
-
Person
和Teacher
的类结构(字段、方法、继承关系)。 -
this.getName()
的调用过程:-
通过
this
找到堆中的对象实例。 -
从元空间查找
Person
类的getName()
方法代码。 -
执行方法,返回
name
字段的值。
-
-
4. this.name
vs this.getName()
的底层操作
-
this.name
-
直接通过
this
引用偏移量访问堆中的name
字段(无需方法调用)。
-
-
this.getName()
-
通过
this
找到对象实例 → 从元空间定位Person.getName()
方法 → 执行方法 → 返回name
字段。
-
super 的作用
-
super
的本质-
并非一个独立引用,而是编译器的语法糖,用于直接访问父类字段或方法(跳过子类重写的方法)。
-
例如,若
Teacher
重写了getName()
,通过super.getName()
可以调用父类Person
的方法。
-
-
内存中的表现
-
super
的访问依赖于元空间中的继承链信息,通过类元数据定位父类方法。
-
总结
-
this
:指向当前对象实例,用于访问字段和方法。 -
super
:语法糖,用于直接访问父类成员,依赖元空间中的继承链。 -
元空间:存储类的元数据,支持方法调用和字段访问的解析。
-
直接访问 vs 方法调用:
-
直接访问字段(
this.name
)更高效,但破坏封装。 -
方法调用(
this.getName()
)符合面向对象设计,可通过内联优化提升性能。
-
四、super注意点
1.this
和 super
不能用于静态上下文(如静态方法或静态代码块)
// this和super都不能使用在静态上下文中。
public static void test(){
System.out.println(this);
System.out.println(super.name);
}
编译错误的原因:
-
静态方法不依赖对象实例
静态方法(static
)属于类本身,而不是类的某个实例对象。在调用静态方法时,无需创建类的实例(如new ClassName()
)。
因此,this
和super
无法指向任何对象实例,因为它们本质上是基于实例的引用。 -
this
和super
的语义-
this
:指向当前对象实例(调用方法的对象)。 -
super
:指向父类对象实例(当前对象的父类部分)。
由于静态方法没有当前对象的概念,使用它们会导致逻辑矛盾。
-
错误示例解释
1. System.out.println(this);
-
错误信息:
'ClassName.this' cannot be referenced from a static context
-
原因:
静态方法中没有隐含的this
对象,无法引用当前实例。
2. System.out.println(super.name);
-
错误信息:
Non-static field 'name' cannot be referenced from a static context
-
原因:
super.name
试图访问父类的实例字段name
,但静态方法中没有父类或子类的实例对象。
解决方案
1. 将方法改为实例方法(移除 static
)
如果方法需要操作实例成员(如 this
或 super
),应将其定义为实例方法:
public void test() {
System.out.println(this); // 正确:指向当前实例
System.out.println(super.name); // 正确:访问父类字段
}
2. 通过对象实例访问成员(不推荐)
如果必须保持方法为静态,需显式传递对象实例作为参数:
public static void test(Person obj) {
System.out.println(obj); // 正确:输出传入的实例
System.out.println(obj.name); // 正确:访问实例字段
}
关键总结
场景 | 是否允许使用 this /super | 原因 |
---|---|---|
实例方法 | ✅ 允许 | 方法调用时已绑定对象实例(this )。 |
静态方法(static ) | ❌ 禁止 | 没有隐含的实例对象,无法指向 this 或 super 。 |
附加说明
-
静态上下文的限制:
静态方法只能直接访问静态成员(静态字段、静态方法),实例成员必须通过对象实例访问。 -
设计原则:
如果方法需要操作实例状态(如字段、方法),应定义为实例方法;如果方法仅处理与类相关的逻辑(如工具方法),才使用静态方法。
2.super什么时候不能省略
在 Java 里,super 关键字用于引用父类的成员(包括属性、方法和构造函数),在以下几种场景中 super 不能省略:
⑴. 调用父类的构造函数
在子类的构造函数中,如果要调用父类的构造函数,必须使用 super 关键字,而且 super() 调用必须是子类构造函数中的第一条语句。
示例代码:
class Parent {
public Parent(int value) {
System.out.println("Parent constructor with value: " + value);
}
}
class Child extends Parent {
public Child(int value) {
// 调用父类的构造函数,super不能省略
super(value);
System.out.println("Child constructor");
}
}
public class Main {
public static void main(String[] args) {
Child child = new Child(10);
}
}
代码解释:Child
类的构造函数中使用 super(value)
调用了父类 Parent
的构造函数。若省略 super(value)
,编译器会尝试调用父类的无参构造函数,若父类没有无参构造函数,就会编译报错。
2. 访问被隐藏的父类属性
当子类定义了与父类同名的属性时,父类的属性会被隐藏。若要在子类中访问父类的该属性,就需要使用 super
关键字。
示例代码1:
class Parent {
int num = 10;
}
class Child extends Parent {
int num = 20;
public void printNumbers() {
// 访问子类的num属性
System.out.println("Child's num: " + num);
// 访问父类被隐藏的num属性,super不能省略
System.out.println("Parent's num: " + super.num);
}
}
public class Main {
public static void main(String[] args) {
Child child = new Child();
child.printNumbers();
}
}
代码解释:Child
类中定义了与父类 Parent
同名的属性 num
。在 printNumbers
方法里,num
访问的是子类的属性,而 super.num
访问的是父类被隐藏的属性,此时 super
不能省略。
示例代码2:
父类Person:
public class Person {
String name;
int age;
String email;
String address;
public Person() {
}
public Person(String name, int age, String email, String address) {
this.name = name;
this.age = age;
this.email = email;
this.address = address;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
}
子类Teacher:
public class Teacher extends Person {
Double sal; //特有属性:工资
String name;
public Teacher(){
}
public Teacher(Double sal) {
this.sal = sal;
}
public Teacher(String name, int age, String email, String address, double sal) {
super();
//super.name = name;
this.name = name;
this.age = age;
this.email = email;
this.address = address;
this.sal = sal;
}
public Double getSal() {
return sal;
}
public void setSal(Double sal) {
this.sal = sal;
}
public void display(){
System.out.println(this.getName());
System.out.println(this.name);
System.out.println(this.getAge());
System.out.println(this.getEmail());
System.out.println(this.getAddress());
System.out.println(this.getSal());
System.out.println("姓名:" + super.name);
System.out.println("年龄:" + super.age);
System.out.println("邮箱:" + super.email);
System.out.println("住址:" + super.address);
System.out.println("工资:" + this.sal);
}
}
测试类Test01:
public class Test01 {
public static void main(String[] args) {
//创建Teacher对象
Teacher t=new Teacher("张三",20,"zhangsan.com","北京朝阳",10000.0);
t.display();//t变量为局部变量
}
}
运行结果:
解释:
父类有一个String name;子类里也有一个String name;而且子类继承了父类。相当于子类有2个String name;
new Teacher时,给Person类的name赋值为NUll,原因是
Teacher构造方法在执行时,
在第一行有一个super(),即使不写也有。
super()会调用父类构造方法。父类无参构造执行
会给Person类赋系统默认值
也即name=null;
此时,内存图为
什么时候用super?
父类里面定义过了一个属性,当然也有可能是方法然后呢,你子类里也定义了一个相同的属性。
但是,你现在就想在子类里访问。父类的name;那么你就必须得用super。当然,反过来,如果你父里边儿有,你子类里边压根就没有定义这个name的话。用不用super都一样。
3. 调用被重写的父类方法
当子类重写了父类的方法后,若要在子类中调用父类的原方法,必须使用 super
关键字。
示例代码:
class Parent {
public void print() {
System.out.println("Parent's print method");
}
}
class Child extends Parent {
@Override
public void print() {
System.out.println("Child's print method");
// 调用父类被重写的方法,super不能省略
super.print();
// super. 什么时候不能省略?父中有,子中有相同的,但是想在子类中访问父的,必须添加 super.
}
}
public class Main {
public static void main(String[] args) {
Child child = new Child();
child.print();
}
}
代码解释:Child
类重写了 Parent
类的 print
方法。在子类重写的 print
方法中,使用 super.print()
调用父类的原方法,super
不能省略。
省了会自动在前面添加this,成死递归了
class Child extends Parent {
@Override
public void print() {
System.out.println("Child's print method");
// 调用父类被重写的方法,super不能省略
this.print();
}
}
3.this可以单独输出,super不能单独输出。
// this本身是一个引用。所以可以直接输出。
System.out.println(this);
// super本身不是一个引用。super只是代表了当前对象的父类型特征那部分。
// super 不能够单独的输出。
//System.out.println(super); // 编译报错。
4.super()语法的作用
Account类:
public class Account {
String actno;
double balance;
public Account()
{
}
public Account(String actno, double balance) {
this.actno = actno;
this.balance = balance;
}
public String getActno() {
return actno;
}
public void setActno(String actno) {
this.actno = actno;
}
public double getBalance() {
return balance;
}
public void setBalance(double balance) {
this.balance = balance;
}
}
CreditAccount 类
public class CreditAccount extends Account {
double credit;
public CreditAccount(Double credit) {
this.credit = credit;
}
public CreditAccount(String actno, double balance, double credit) {
//new Account(actno,balance);//不推荐,会开辟新空间
//super(actno, balance); super()语法的作用
this.actno=actno;
this.balance=balance;
this.credit = credit;
}
public double getCredit() {
return credit;
}
public void setCredit(double credit) {
this.credit = credit;
}
}
// 通过子类的构造方法调用父类的构造方法。 // 引入这个语法的作用:1.代码复用。2.为了“模拟”现实世界中的要有儿子,得先有父亲。3.通过子类构造方法调用父类构造方法是为了给继承过来的父类型特征初始化。
测试类Test:
public class Test {
public static void main(String[] args) {
CreditAccount ca=new CreditAccount("act001",0.0,0.999);
System.out.println("账号:"+ca.getActno());
System.out.println("余额:"+ca.getBalance());
System.out.println("信用度:"+ca.getCredit());
}
}
5.super()的默认调用
①当一个构造方法第一行没有显示的调用“super(实参);”,也没有显示的调用“this(实参)”,系统会自动调用super()。因此一个类中的无参数构造方法建议显示的定义出来。
②super(实参); 这个语法只能出现在构造方法第一行。
③在Java语言中只要new对象,Object的无参数构造方法一定会执行。
在Java中,构造方法的调用遵循特定规则,确保对象初始化过程的正确性。以下是对相关规则的总结:
-
隐式调用
super()
:-
如果构造方法的第一行没有显式调用
super(参数)
或this(参数)
,编译器会自动插入super()
(调用父类的无参构造方法)。 -
注意:若父类没有无参构造方法且子类未显式调用父类的其他构造方法,会导致编译错误。
-
-
显式定义无参构造方法的建议:
-
当父类没有无参构造方法时,子类必须显式调用父类的有参构造方法(通过
super(参数)
)。 -
显式定义无参构造方法可避免因父类构造方法变动导致的子类错误,提高代码健壮性。
-
-
super(参数)
和this(参数)
的位置:-
必须出现在构造方法的第一行,否则编译错误。
-
两者不能同时存在,因为每个构造方法只能直接调用一个父类或本类的构造方法。
-
-
Object类的无参构造方法:
-
所有类最终继承自
Object
,因此任何对象的实例化都会触发Object
的无参构造方法。 -
构造方法调用链会从子类逐级向上,最终执行
Object
的无参构造方法。
-
示例说明:
class Parent {
Parent(int x) {} // 父类没有无参构造方法
}
class Child extends Parent {
Child() {
super(10); // 必须显式调用父类的有参构造方法
}
}
若子类Child
未显式调用super(10)
,编译器会尝试调用super()
,但父类Parent
没有无参构造方法,导致编译失败。
结论:
-
构造方法需确保父类初始化正确,隐式或显式调用父类构造方法是强制要求。
-
显式定义无参构造方法有助于维护继承结构的灵活性。
-
Object
的无参构造方法是所有对象初始化的终点,确保其必然执行