当子类定义了与父类相同的某个字段时,可以称之为属性覆盖。此时可以将父类的该字段和子类的该字段完全独立来看。这是两个完全不同的属性。
-
方法重写 : 当父类对象执行子类的时候,若子类重写了父类的方法,则父类调用该方法时,会执行子类的方法。
-
字段隐藏 : 如子类声明了一个与父类相同的字段,这个字段在子类中隐藏了父类的字段。 当父类对象获取该属性值时 会返回父类的值, 而子类对象获取该属性值时 会返回子类的值
请看如下代码
Father father = new Son();
System.out.println(father.x);//输出father的x 而不是son的x
此时Son覆盖了father的x属性, 为什么输出的是 Father
的 x
值而不是 Son
的 x
值呢?
-
字段的隐藏(Hiding):在Java中,如果子类声明了一个与父类同名的字段,这个字段在子类中隐藏了父类的字段。但是,这种隐藏只影响通过子类类型的引用访问字段的情况。当通过父类类型的引用访问字段时,访问的是父类的字段。
-
方法重写(Overriding):这与字段隐藏不同。如果子类重写了父类的方法。通过父类或子类类型的引用调用此方法时,都会调用子类的方法(如果引用指向的是子类对象)。
具体代码如下
package test;
/**
* @author yufen
* @date 2024/11/20 10:22
*/
public class Test {
public static void main(String[] args) {
Father father = new Father();
System.out.println(father.x);
//以上结果
// Father.x = 10
//20
System.out.println("=========分割线=======");
Father father2 = new Son();
//创建Son实例时 , 会先执行父类的构造器方法 字节码中的<init>
//父类的init方法中包含 属性的显示赋值,代码块中的代码执行 和 构造器里的代码执行。
//父类的init的执行步骤:
//1.先执行父类的x设为10
//2.再执行this.print()方法:此时 this.print()方法指向了son的print()方法
// 此时执行son的print()方法,打印son的x , 此时son的x 还没有初始化 还等于0 。 因此打印 son.x = 0
// 最后将 father的x赋值为20
//3.执行子类的init方法
//执行子类的init方法执行过程
//4.子类的属性x显示赋值为30
//5.执行子类的构造器方法
//构造器方法:先执行 this.print()方法,打印出son.x = 30 此时son的x是30
//再将x赋值为40
System.out.println("--------"+father2.x);//此处father2.x 会返回Father类对象的x 而不是子类对象的x
//=========分割线=======
//以上结果
//Son.x = 0
//Son.x = 30
//20
}
}
class Father{
public Father(){
this.print();
x = 20;
}
int x = 10;
public void print(){
System.out.println("Father.x = " + x);
}
}
class Son extends Father{
int x = 30;
public Son(){
this.print();
x = 40;
}
public void print(){
System.out.println("Son.x = " + x);
}
}
总结:若子类有一个属性,和父类的某个属性同名,则该属性和父类的同名属性是完全独立的。如通过父类类型 Father father = new Son() 的引用访问字段,则返回父类的字段。如通过子类类型 Son son = new Son() 的引用访问字段,则返回子类的字段
若子类的属性是由父类继承得到的,则会获取和改变父类属性的值。