一、变量隐藏——变量不具有多态性
Java 中的变量不遵循多态性,所以重写仅适用于方法,而不适用于变量。 并且,当子类中的实例变量与父类中的实例变量具有相同的名称时,则从引用类型中选择该实例变量。
在 Java 中,当子类中的实例变量与父类中的实例变量具有相同的名称时,子类的变量将隐藏父类的变量,即使它们的类型不同
。 这种概念称为可变隐藏。
在变量隐藏中,子类隐藏继承的变量而不是替换它们,这基本上意味着子类的对象包含两个变量,而子变量则隐藏了父变量。我们可以通过 super.x 来访问父类中的变量
。
示例:
class Parent {
String x = "Parent`s Instance Variable";
public void print() {
System.out.println(x);
}
}
class Child extends Parent {
String x = "Child`s Instance Variable";
@Override
public void print() {
System.out.print(x);
// 可以通过 super.x 访问父类被隐藏的变量
System.out.print(", " + super.x + "\n");
}
}
Parent parent = new Child();
System.out.println(parent.x)
Child child = new Child();
System.out.println(child.x)
结果:
Parent`s Instance Variable
Child`s Instance Variable
解析:
在 Java 中,当我们在Child类中使用已经用于在Parent类中定义变量的名称定义变量时,Child类的变量将隐藏父类的变量。
因此当我们尝试通过持有子对象来从父对象的引用访问该变量时,将调用父类中的变量
。因此我们知道实例变量是从引用类型而不是实例类型中选择的,并且多态性不适用于变量
。
为什么变量被设计为跟随隐藏而不是覆盖?
因为如果我们在子类中更改其类型,则变量覆盖可能会破坏从父级继承的方法。
每个子类都从其父类继承变量和方法(状态和行为)。 如果 Java 允许变量覆盖,它将破坏使用该变量的任何方法,并且由于子代已从父代继承了这些方法,因此编译器将在子类中给出错误。
二、方法重写——方法具有多态性
在方法覆盖的情况下,覆盖方法完全替换了继承的方法,因此当我们尝试通过持有子对象来从父对象的引用访问该方法时,将调用子类中的方法。
我们可以通过 super.x() 来访问父类中的方法
。
三、super 的使用
1、子类调用被隐藏的父类的变量
此时子类中有一个和父类一样的字段(父类字段被隐藏),
为了获得父类的这个字段我们就必须加上super
。
我们通过 super 是不能访问父类 private 修饰的变量
的,因为这个只属于父类的内部成员,一个对象是不能访问它的 private 成员的。
示例:
public class A {
String nameA="A";
}
public class B extends A{
String nameA="B";
public void getName() {
System.out.println("子类"+nameA);
System.out.println("父类"+super.nameA);
}
public static void main(String[] args) {
B b=new B();
b.getName();
}
2、子类调用被重写的父类的方法
在子类中,我们重写了父类的方法,如果我们想去调用父类的相同方法,必须要通过 super 关键字显示的指明出来。如果不明确出来,按照子类优先的原则,相当于还是再调用被重写的方法
示例:
public class A {
private String nameA="A";
public void getName() {
System.out.println("父类"+nameA);
}
public static void main(String[] args) {
}
}
public class B extends A{
private String nameB="B";
@Override
public void getName() {
System.out.println("子类"+nameB);
super.getName();
}
public static void main(String[] args) {
B b=new B();
b.getName();
}
}
3、调用父类的构造方法
因为父类的构造函数不能被继承,因此编译器会自动在子类构造函数的第一句加上 super() 来调用父类的无参构造器
,此时可以省略不写,如果想写上的话必须在子类构造函数的第一句
。可以通过 super 来调用父类其他重载的构造方法
,只要相应的把参数传过去就好。
注意:子类继承父类的时候会自动继承父类的默认构造函数(也就是继承那个无参数的构造函数)。如果你的类里面已经有一个带有参数的构造函数了,而你没有写那个默认的不带参数的构造函数的话,继承的时候子类就会报错,因为系统不知道要不继承哪个构造函数,必须明确的使用super()关键字来描述。所以我们一般为了避免这种错误的发生,在有带有多个构造函数的类里面都会写一个不带参数的构造函数
。
示例:
public class A {
private String nameA="A";
public A() {
}
public A(String nameA) {
this.nameA = nameA;
}
}
public class B extends A{
private String nameB="B";
public B() {
super("lhj");
}
}
}