java笔记——inheritance之字段继承

之前讲过子类继承父类方法的种种疑难点,那么成员变量又是怎样被继承的呢?
我们都知道方法继承里有override,一旦子类中的方法被重写了,父类中的原方法就对于当前子类调用来说不可见了。那字段值是否遵循这个规则呢?
先来看一段很简单的代码

class Test{
  public static void main(String[] args){
    Son s = new Son();
    s.show();
    System.out.println(Son.name);
  }
}
class Father {
  String name = "father";
  public void show(){
    System.out.println( name );
  }
}
class Son extends Father{
  String name = "son";
}

运行结果:
father
son

初学者在这里会有疑问:为什么son这个类调用的show打印的确实father呢?

首先我们要明确,show这个函数是Son从Father那边继承得到的,但是这个show的最终归属,还是Father.
就相当于儿子从爸爸那里继承得到了一套豪华大别墅,但是房产证上名字写的是爹的(ˉ▽ˉ;)…
所以在执行s.show()的时候,这个show函数实际上还是属于Father类里面的,只不过在Son这个类里面被调用罢了。

有了这个概念之后我们来着重看一下字段值name
首先在子类父类里面都有一摸一样的字段值name,一个内容是”father“,另一个是”son“。对于Son子类来说,父类Father的name也会被继承得到
那么有人就会问,有两个一摸一样的字段值,那在子类里面,name到底指谁???

从上面的程序来看,s.name输出的是son,貌似name指的是自己的那个name.
也就是this.name

实际上,在子类Son的堆里面,现在有两个name,一个是自己的”son“,还有一个是继承得到的”father“,这两个name同时存在,并且都能够被子类Son看到,只不过继承得到的”father”不是被优先考虑的。 当使用s.name去调用的时候,优先被考虑的是子类的name “son”。
那么我们怎样去调用从父类继承得到的同名字段呢?方法有两种:

1.向上转型
2.父类的方法

class Test{
  public static void main(String[] args){
    Son s = new Son();
    System.out.println(((Father)s).name);//通过向上转型直接访问
    s.show();//通过父类的方法调用
  }
}
class Father {
  String name = "father";
  public void show(){
    System.out.println( name );
  }
}
class Son extends Father{
  String name = "son";
}

运行结果:
father
father

也就是我们可以通过((Father)s).name来访问到“father”;也可以通过Father中的方法(就像上面这个例子),因为子类的字段对于父类来说是不可见的,所以调用的字段name就是唯一的。

为了类比类的方法继承和字段继承的不同,我们再来看一个比较直观的例子:

public class ClassA {
	int count=2;
	
	public void display(){
		System.out.println(this.count);
	}
}
public class ClassB extends ClassA {
    int count=20;
     
    public void display(){
        System.out.println(this.count);
    }
     
    public static void main(String[] args){
        ClassA a=new ClassB();
        System.out.println(a.count);
        a.display();
    }
}

运行结果:
2
20

我么先来看看第一行的输出,为什么会是A类里面的2呢?

 ClassA a=new ClassB();
 System.out.println(a.count);

首先,a这个对象声明是A类,但是却给了它一个B的实例(隐式转型)。现在的a其实就是一只披着 羊皮的狼,表面上它是A类,实则指向的是B类的一个实例。这是很基础动态绑定。
那么现在a.count到底是A的还是B的?
答案是A的。因为这就是我上面说的子类调用父类重名字段的第一种方法——向上转型
这段代码是不是可以等价于:

ClassB b=new ClassB();
System.out.println(((ClassA)b).count);

因此这里的a.count实际上是把一个classB的实例向上转型之后进行调用,自然调用到的是classA里面的count。

再来看第二行,a.display()打印的就是classB的count了。别忘了同名方法会被override哦~

public class ClassB extends ClassA {
    int count=20;
     
    public void display(){//把父类中的display重写
        System.out.println(this.count);
    }
     
    public static void main(String[] args){
        ClassA a=new ClassB();
        System.out.println(a.count);
        a.display();//a实际上是一个对ClassB实例的引用,因此a.display()就是ClassB里重写的那个
    }
}

结论(敲黑板(●ˇ∀ˇ●))

对于字段:

子类里面,和父类字段名(数据类型可以一致也可以不一致)一致的字段,不会发生覆盖,两个同名字段均会在子类的内存堆中存在,且都可见。但是子类中的字段会被优先访问。此时可以通过向上转型、调用父类方法、调用super 来访问父类的同名字段。

对于方法:

子类里面,和父类中函数的函数签名(函数名,函数参数的数量和类型)一致的方法,会进行override,
父类的原方法将会被覆盖 ,此时原方法不可见。

怎样去访问被覆盖的方法?
利用super调用,无法使用向上转型和父类函数来调用,此时父类也看不见原方法。

你可能感兴趣的:(java)