当前位置: 首页 > 编程笔记 >

Java中构造器内部的多态方法的行为实例分析

华甫
2023-03-14
本文向大家介绍Java中构造器内部的多态方法的行为实例分析,包括了Java中构造器内部的多态方法的行为实例分析的使用技巧和注意事项,需要的朋友参考一下

本文实例讲述了Java中构造器内部的多态方法的行为操作。分享给大家供大家参考,具体如下:

这篇文章主要讨论的是,若在一个构造器中调用正在构造的对象的某个动态绑定的方法时会出现的情况。在此之前,我们需要知道构造器是如何在复杂的层次结构中运作的,尽管构造方法并不具有多态性,因为它们实际上是static方法,只不过是隐式声明的static。

复杂层次结构中构造器的调用顺序

基类的构造器总是在导出类的构造过程中被调用,而且按照继承层次逐渐向上链接,以使每个基类的构造器都能得到调用。这样做是因为,在Java类中,我们通常将字段设置为private类型,也就是说,在子类中通常无法直接访问基类的字段,那么只有通过调用基类的构造器才能对基类对象的元素进行初始化,那么就必须保证所有的构造器都得到调用,这样才能正确地构造完整的对象。下面的例1展示了包含有组合与继承关系的各类中构造器的调用顺序: 

例1:

class Meal {
   Meal() { System.out.println("Meal()"); }
}
class Bread {
   Bread() { System.out.println("Bread()"); }
}
class Cheese {
   Cheese() { System.out.println("Cheese()"); }
}
class Lettuce {
   Lettuce() { System.out.println("Lettuce()"); }
}
class Lunch extends Meal {
   Lunch() { System.out.println("Lunch()"); }
}
class PortableLunch extends Lunch {
   PortableLunch() { System.out.println("PortableLunch()");}
}
public class Sandwich extends PortableLunch {
   private Bread b = new Bread();
   private Cheese c = new Cheese();
   private Lettuce l = new Lettuce();
   public Sandwich() { System.out.println("Sandwich()"); }
   public static void main(String[] args) {
    new Sandwich();
   }
}

例1反映了关于Meal、Lunch和Sandwich之间三层继承关系(不包含Object类),以及Bread、Cheese和Lettuce与Sandwich的组合关系。在main函数中创建一个Sandwich对象后,我们就可以看到输出结果: 

这说明在复杂的层次结构中构造器的调用遵从的顺序为: 

** 1、调用基类构造器。这个步骤会不断地反复递归下去,首先是构造这种层次结构的根,然后是下一层导出类,等等,直到最低层的导出类;
2、按声明的顺序调用成员的初始化方法;
3、调用导出类构造器的主体。**

构造器内部的多态方法的行为

那么,现在我们回到文章开头提到的问题,若在一个构造器中调用正在构造的对象的某个动态绑定的方法,会出现什么情况呢?我们知道,动态绑定(或后期绑定)的方法的调用是在运行时才决定的,因为对象在html" target="_blank">程序运行之前无从得知它自己到底是基类的对象,还是某个导出类的对象。如果在基类的构造器内部调用某个动态绑定方法,该方法是被导出类覆盖的,那么这便可能产生难以预料的后果,因为该导出类的对象还未被完全构造,但它的方法却被调用了。我们可以通过例2看到问题所在:

例2:

class Glyph {
   void draw() { System.out.println("Glyph.draw()"); }
   Glyph() {
    System.out.println("Glyph() before draw()");
    draw();
    System.out.println("Glyph() after draw()");
   }
}  
class RoundGlyph extends Glyph {
   private int radius = 1;
   RoundGlyph(int r) {
    radius = r;
    System.out.println("RoundGlyph.RoundGlyph(), radius = " + radius);
   }
   void draw() {
    System.out.println("RoundGlyph.draw(), radius = " + radius);
   }
}  
public class PolyConstructors {
   public static void main(String[] args) {
    new RoundGlyph(5);
   }
}

运行结果:

在运行结果中,我们看到,基类Glyph的构造器中调用了被子类RoundGlyph覆盖的draw()方法,并且输出了radius=0,这显然是一个错误,因为这个“0”是在其他任何事物发生之前,系统分配给对象的存储空间的初始值——二进制的零,而非我们想要设定的初始值“1”。这是因为,我们在创建子类(RoundGlyph)对象时会先调用基类(Glyph)的构造器构造基类对象,而在基类的构造器中却调用了被子类覆盖的动态绑定的方法(draw()),而这个方法所操纵的可能是子类中的还未进行初始化的成员(radius),这便会招致灾难,尽管编译器并没有报错。

因此,在编写构造器中有一条有效的准则:“用尽可能简单的方法使对象进入正常状态;如果可以的话,避免调用其他方法”。在构造器中,唯一能够安全调用的是基类中的final方法(包括private方法),因为这些方法不能被子类覆盖,也就不会出现上述的问题。

更多java相关内容感兴趣的读者可查看本站专题:《Java面向对象程序设计入门与进阶教程》、《Java数据结构与算法教程》、《Java操作DOM节点技巧总结》、《Java文件与目录操作技巧汇总》和《Java缓存操作技巧汇总》

希望本文所述对大家java程序设计有所帮助。

 类似资料:
  • 问题内容: 码: 输出: 为什么以这种方式打印? 问题答案: 当您在运行时调用多态方法时,Java使用特殊的数据结构来决定需要调用哪个类的方法。在构造对象时,即 在执行用户提供的任何构造函数和初始化程序代码之前,都会 建立此结构。 创建时,在输入的构造函数 之前 已准备好“何时调用,则需要调用或” 的数据结构。由于此结构是为类准备的,因此即使调用代码在内部的构造函数中,它也指向。这就是为什么您看到

  • 本文向大家介绍Java中的多态用法实例分析,包括了Java中的多态用法实例分析的使用技巧和注意事项,需要的朋友参考一下 本文实例讲述了Java中的多态用法。分享给大家供大家参考。具体分析如下: 多态,是面向对象的程序设计语言最核心的特征。封装性、继承性都比较简单,所以这里只对多态做一个小小的笔记。。。 1、什么是多态? 多态意味着一个对象可以多重特征,可以在特定的情况下,表现出不同的状态,从而应对

  • 问题内容: 我将如何为正在实现接口的内部类编写构造函数?我知道我可以上一门全新的课,但是我认为必须有一种方法可以做到这一点: 当我输入此内容时,它不会将AbstractAction方法识别为构造函数(编译器要求返回类型)。有人有主意吗? 问题答案: 只需在扩展类的名称后面插入参数即可: 另外,您可以使用初始化块:

  • 上一个小节我们学习了 Python 的类属性和实例属性的知识点,这个小节我们还是会学习关于类的知识。这节课我们会学习类的构造方法、析构方法和实例方法。 1. 实例方法 1.1 定义 实例方法是用于访问对象实例属性的方法,语法如下: class 类: def 实例方法(self): self.属性 实例方法定义在类中,它的第一个参数 self 指向调用该方法的对象,在实例方

  • 本文向大家介绍java 将方法作为传参--多态的实例,包括了java 将方法作为传参--多态的实例的使用技巧和注意事项,需要的朋友参考一下 在前段时研究智能算法时,发现如果使用java进行实现的话,往往具体实现过程差不多,但是适应值函数却根据 研究对象的不同发生很大的改变,这样对代码的维护产生很大的阻碍,于是产生的一个疑问:可不可以将适应值函数 作为参数传入到方法中,根据C/C++的习惯的话,由于

  • 《Java》中内在类的确切目的及其创作方法。如果我在类中创建方法而不是在内部类中创建那些方法,我能得到相同的行为吗? 我们既可以访问内部类方法,也可以访问外部类之外的实例方法。那么内类在Java的确切目的是什么呢?有没有一种情况/可能性,没有内在的类,我们就无法生存?