qq_36152559
2017-03-12 10:48
采纳率: 100%
浏览 1.0k
已采纳

寻求Java多态内存分配以及多态中成员方法的特点

情况一:
class A{
int a = 0;

public int getA() {
    return a;
}
public void setA(int a) {
    this.a = a;
}

}
class B extends A{

  public void static main(String[] args){
           A a = new B();
    }

}
情况二:
class A{
int a = 0;

public int getA() {
    return a;
}
public void setA(int a) {
    this.a = a;
}

}
class B extends A{
int a=3;
public void static main(String[] args){
A a = new B();
}
}
在这两种情况下对内存分别是如何分配的,请大神解惑?

  • 点赞
  • 写回答
  • 关注问题
  • 收藏
  • 邀请回答

1条回答 默认 最新

  • blownewbee 2017-03-12 10:50
    已采纳

    http://blog.csdn.net/xiaochuhe_lx/article/details/9126325

     Person worker = new Worker(); 子类实例对象地址赋值给父类类型引用变量。多态的体现。
    
    多态中成员方法的特点分析:
    
    【子类有,父类没有】编译失败!!!
    worker.startWork(); 为什么编译不通过呢?提示:找不到符号。
    因为引用变量worker是Person类型,在Person类的方法表中查找方法startWork(),找得到吗?找不到,更别提常量池解析了。编译失败。
    
    
    【子类有,父类有,重写,非静态】调用子类!!!
    worker.say(); 子类重写父类方法,被重写的方法在子类跟父类的方法表中索引相同。
    调用的时候,在父类类型的方法表中查找say方法的索引,然后把索引存到PolDemo类的常量表中(常量池解析,就是用Person类方法表中的索引项来代替常量池中的符号引用)。
    因为索引相同,直接拿常量表中say方法的索引去子类方法表中调用say()方法。 所以,此时调用的是被子类重写的方法。见图中的内存分配。
    
    
    【子类有,父类有,静态】调用当前引用类型变量类型中的方法。
    因为静态是属于类的,由实例共享,所以只看当前引用变量所属的类中的静态方法。
    
    
    多态中(父类引用指向子类对象)成员方法(非静态)有以下特点:
    编译期根据引用类型变量所属的类型方法表中是否有调用的方法,没有编译失败。
    运行期根据在堆中创建对象的实际类型找到对应的方法表,从中确定具体的方法在内存中的位置。
    
    
    堆中实例对象:子类包含父类,子类对父类说:你的就是我的,我的还是我的。
    
    
    多态中成员变量的特点分析:
    无论编译期还是运行期,都只参考引用类型变量所属的类中是否有对象的成员变量。
    
    
    小的总结:
    再来看这条语句:Person worker = new Worker(): 多态,父类引用指向子类对象。
    跟这句代码的作用是一样的,Worker w = new Worker(); Person worker = w; 就是子类的引用传给父类类型的引用。向上转型,都指向一个实例对象(子类)。
    1:为什么可以将子类实例对象的引用传给父类类型引用呢? 
    答:多态的体现,鸽子是鸟类,鹦鹉是鸟类,喜鹊是鸟类,它们都是鸟类,这就是鸟类的多种表现形态(多态),
    它们有鸟类的基本特征与行为,并且还有自己特有的行为,那就会把鸟类的基本特征与行为继承过来给自己。extends关键字声明了他们的关系。
    程序中这句话 鸟类 xx鸟 = new 鸽子();就创建了一个鸟类的引用,它是鸽子。
    类的概念是抽象的,所以它就仅仅是个引用,只有引用到实例对象,它才真正实现了自己。
    
    
    2:父与子的关系会怎样呢?
    由于Worker类继承自Person类,所以会先加载Person类并初始化成员变量在Worker类实例对象中。这就是子类继承过来的成员变量。
    如果子类中定义了与父类相同的成员变量,变量不会存在重写,子类实例对象中会有两份相同名称的实例变量,调用时,父类的会被隐藏。
    如果想调用父类被隐藏的成员变量,那就要使用super关键字。
    同样,子类会继承父类的成员方法作为自己的成员方法,如果子类定义了与父类相同的成员方法(重写),多态中会调用子类自己的成员方法。
    
    
    3:多态,又有什么好处呢?跟这样写(Worker worker = new Worker();)有什么区别,不一样继承父类的属性方法吗?
    首先明确: Worker worker = new Worker();这样写仅仅是继承关系。不存在多态特性。
    Person worker = new Worker(); 这样写就展示了多态的关系,鸽子是鸟类的一种表现形态。
    有啥好处?一句话概括:提高了程序的扩展性,表现在什么地方呢?比如:
    现在有个工厂,能解析鸟类的语言,鸽子是鸟类吧?Ok,那建一个工厂,让鸽子说。
    public static void doWork(Dove dove) {
    String sound = dove.say(); // ... 拿到鸽子说的话
    }
    鹦鹉是鸟类吧?Ok,那建一个工厂,让鹦鹉说。
    public static void doWork(Parrot parrot) {
    String sound = parrot.say(); // ... 拿到鹦鹉说的话
    }
    如果还有一千个鸟类要说话,那我岂不得建一千座工厂?。。。累不累?那既然都是鸟类,我建一个鸟类的工厂不就行了嘛?
    public static void doWork(Bird bird) {
    String sound = bird.say(); // ... 拿到xx鸟说的话。。。
    }
    这时候,无论你什么鸟进来,我都让你说话,而且说得都是你自己的话,因为你继承我并重写了你的功能。
    -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
    Worker w = (Worker)worker; // 向下转型。目的,为了调用子类非继承父类、自己特有的方法。
    因为多态有个弊端,只能使用父类的引用访问父类的成员。所以向下转型是为了访问子类自己的成员。
    首先,worker引用指向的实例对象本来就是子类类型的。所以赋值给子类类型引用变量非常可以。
    然后现在用子类类型的引用就可以访问自己的成员方法了啦啦啦。
    
    点赞 打赏 评论

相关推荐 更多相似问题