Java多态之向上转型、同名变量以及方法覆盖

来源:blog.csdn.net 更新时间:2023-05-25 21:55
首先,我们先要简单理解两个概念:

1、向上转型:父类类型的引用指向子类类型的对象。例如:

class Parent {
 
}
 
class Child extends Parent {
 
}
 
public class MainClass {
 
    public static void main(String[] args) {
        Parent parent = new Child();//向上转型
 
    }
 
}
2、向上转型中的多态体现,先看例子:
class Parent {
void myType() {
    System.out.println("I`m Parent.");
}
}
 
class Son extends Parent {
    void myType() {
        System.out.println("I`m Son.");
    }
}
 
class Daughter extends Parent {
    void myType() {
        System.out.println("I`m Daughter.");
    }
}
 
public class MainClass {
 
    public static void main(String[] args) {
        Parent parent_Parent = new Parent();
        Parent parent_Son = new Son();
        Parent parent_Daughter = new Daughter();
        System.out.print("parent_Parent:");
        parent_Parent.myType();
        System.out.print("parent_Son:");
        parent_Son.myType();
        System.out.print("parent_Daughter:");
        parent_Daughter.myType();
 
    }
 
}
输出结果:
parent_Parent:I`m Parent.
parent_Son:I`m Son.
parent_Daughter:I`m Daughter.

可以看到,Parent类型引用了不同的对象,调用了相同的方法,输出了不一样的结果。向上转型中的多态体现可以用在函数传递方面,如果函数参数的类型为Parent,我们可以传递给它Parent,Son,Dauther类型的对象。

在学习下面关于Java向上转型的同名变量以及方法覆盖前,希望您能了解Java程序初始化顺序,您先看看这篇博文:点击打开链接 的例子,将非常有助于您对一下例子的理解。

我们先记住这几个知识点:

一、如果使用父类类型的引用指向子类的对象,该引用只能调用父类中定义的方法和变量;

二、如果子类中重写了父类中的一个方法,那么在调用这个方法的时候,将会调用子类中的这个方法;

三、变量不能被重写(覆盖),”重写“的概念只针对方法。



接下来,我们逐点分析:

一、如果使用父类类型的引用指向子类的对象,该引用只能调用父类中定义的方法和变量;

对于第一点,可以在编写程序的时候发现,对于一个父类类型引用了一个子类的对象,在编译器,比如eclipse,不会提示父类没有而子类有的方法,读者可以去试一试。



二、如果子类中重写了父类中的一个方法,那么在调用这个方法的时候,将会调用子类中的这个方法;

先看一个简单的例子:

class Parent {
    public String ap = getStr();//调用的是Child中的getStr()方法,1
 
    public Parent() {
        System.out.println("parent_construct");
        System.out.println("ap:"+ap);//2
    }
 
    public String getStr() {//这个方法不会被调用
        System.out.println("parent_method");
        return "Parent";
    }
 
}
 
class Child extends Parent {
 
    String ac = getStr();//调用的是自己getStr()的方法
 
    public Child() {
        System.out.println("Child_construct");
        System.out.println("ac:"+ac);
    }
 
    public String getStr() {//这个方法被调用两次
        System.out.println("child_method");
        return "Child";
    }
}
 
public class MainClass {
    
    public static void main(String[] args) {
        Parent parent = new Child();
 
    }
 
}
输出结果:
child_method
parent_construct
ap:Child
child_method
Child_construct
ac:Child

这里要注意的是第一个输出,对应程序中注释为1的地方。同时,由于调用的是子类的getStr()方法,所以ap的值为CHild,因此,第三行输出的是Child,对于程序中注释为2的地方。所以,如果子类中重写了父类中的一个方法,那么在调用这个方法的时候,将会调用子类中的这个方法;



三、如果父类和子类中存在同名变量会是个什么情况?

我们先看一个例子,

class Parent {
    public String a = getAParent();
 
    public Parent() {
        System.out.println("parent_construct:"+a);
        getStr();//调用的是子类的getStr()方法,1
    }
 
    public void getStr() {
        System.out.println("parent_method");
        System.out.println("ap:"+a);
    }
    public String getAParent() {
        System.out.println("getAParent");
        return "pA";
    }
 
}
 
class Child extends Parent {
 
    public String a = getAChild();
 
    public Child() {
        System.out.println("Child_construct:"+a);
        getStr();
    }
 
    public void getStr() {//被调用两次,这里的变量a是子类的变量a,2
        System.out.println("child_method");
        System.out.println("ac:"+a);
    }
    public String getAChild() {
        System.out.println("getAChild");
        return "cA";
    }
}
 
public class MainClass {
    int a;
    public static void main(String[] args) {
        Parent parent = new Child();
 
    }
 
}
输出结果:
getAParent
parent_construct:pA
child_method
ac:null
getAChild
Child_construct:cA
child_method
ac:cA
输出的第四行竟然是null,好像很奇怪,实则不奇怪。在代码中注释为1的地方,调用的是子类的getStr()方法,而getStr()函数中的变量a是子类的变量a,而不是父类中已经被赋值为字符串“pA”的变量a,由于Java初始化顺序,在子类中的getStr()方法这次被调用的时候,子类中的变量a还没有被赋值为“cA”.

我们再看看子类中没有同名变量a 的情况,我们直接把对应的那一行代码注释掉。

class Parent {
    public String a = getAParent();
 
    public Parent() {
        System.out.println("parent_construct:"+a);
        getStr();//调用的是子类的getStr()方法,1
    }
 
    public void getStr() {
        System.out.println("parent_method");
        System.out.println("ap:"+a);
    }
    public String getAParent() {
        System.out.println("getAParent");
        return "pA";
    }
 
}
 
class Child extends Parent {
 
    //public String a = getAChild();//子类中没有同名变量a
 
    public Child() {
        System.out.println("Child_construct:"+a);
        getStr();
    }
 
    public void getStr() {//被调用两次,这里的变量a是子类的变量a,2
        System.out.println("child_method");
        System.out.println("ac:"+a);
    }
    public String getAChild() {
        System.out.println("getAChild");
        return "cA";
    }
}
 
public class MainClass {
    int a;
    public static void main(String[] args) {
        Parent parent = new Child();
 
    }
 
}
输出结果:
getAParent
parent_construct:pA
child_method
ac:pA
Child_construct:pA
child_method
ac:pA

输出的第四行变成了“pA”,说明在调用子类的getStr()方法时,里面的变量是父类中的变量a,这不是废话吗:)

我们最后来看一下如果父类和子类中存在同名变量,内存是不是给两个变量都分配了空间。

class Parent {
    public String a = getAParent();
 
    public Parent() {
        System.out.println("parent_construct:"+a);
        getStr();
    }
 
    public void getStr() {
        System.out.println("parent_method");
        System.out.println("ap:"+a);
    }
    public String getAParent() {
        System.out.println("getAParent");
        return "pA";
    }
    
    public void getParentA() {//
        System.out.println("ParentA always is :"+a);
    }
 
}
 
class Child extends Parent {
 
    public String a = getAChild();
 
    public Child() {
        System.out.println("Child_construct:"+a);
        getStr();
    }
 
    public void getStr() {
        System.out.println("child_method");
        System.out.println("ac:"+a);
    }
    public String getAChild() {
        System.out.println("getAChild");
        return "cA";
    }
    public void getParentB() {//
        System.out.println("ParentA always is :"+a);
    }
}
 
public class MainClass {
    int a;
    public static void main(String[] args) {
        Parent parent = new Child();
        parent.getParentA();
        Child child = (Child)parent;//1
        child.getParentB();
        System.out.println("Parent类型 a:"+parent.a);
        System.out.println("Child类型 a: "+child.a);
    }
}
输出结果:
getAParent
parent_construct:pA
child_method
ac:null
getAChild
Child_construct:cA
child_method
ac:cA
ParentA always is :pA
ParentA always is :cA
Parent类型 a:pA
Child类型 a: cA

说明了内存给两个变量都分配了空间。根据最后两行的输出结果,说明如果直接调用变量a,a所属的类与类型有关,而与具体的引用对象无关。