zoukankan      html  css  js  c++  java
  • 【Thinking in Java】编写构造器时应注意:尽量避免调用其他非private方法

      最近重温了《Thinking in Java》,发现了一个让我为之兴奋的知识漏洞,必须得分享一下。

      上一篇的《Java类初始化的过程》的随笔中,那个初始化顺序并不完整。初始化的实际过程是:

      1.   在其他任何事物发生之前,将分配给对象的存储空间初始化成二进制的0;
      2.   如上一篇的《Java类初始化的过程》那样:父类的static成员变量和方法-->该类的static变量和方法-->开始实例化-->父类的普通成员变量和方法-->父类的构造方法-->该类的普通成员变量和方法-->该类的构造方法-->实例化结束

      然而,我们知道,当子类Sub继承了父类Sup、并重写了父类的方法draw()后,我们即使向上转型为父类(即Sup sup=new Sub()),当我们调用sup.draw()方法的时候,它实际上调用的是Sub的draw方法。这里就有个坑了!

      如果在父类的构造方法里调用draw()方法,从逻辑上,我们以为是调用父类的draw()方法,而实际上,即使在父类的构造器以内,Java编译器让它调用的还是子类的draw()方法。

      我们用一个例子来展示一下:

      

    public class Glyph {
        Glyph(){
            System.out.println("Glyph before draw()");
            draw();         //逻辑上本应该调用本类的draw(),然而结果不是
            System.out.println("Glyph after draw()");
        }
        void draw(){
            System.out.println("Glyph.draw()");
        }
    }
    
    public class RoundGlyph extends Glyph{
        private int radius=1;
        public RoundGlyph(int r) {
            radius=r;
            System.out.println("RoundGlyph.RoundGlyph(),radius="+radius);
        }
    
        @Override
        void draw() {
            System.out.println("RoundGlyph.draw(),radius="+radius);
        }
    }
    
    public class PolyConstructors {
        public static void main(String[] args) {
            new RoundGlyph(5);
        }
    }

      打印结果是: 

      Glyph before draw()
      RoundGlyph.draw(),radius=0
      Glyph after draw()
      RoundGlyph.RoundGlyph(),radius=5

      看打印的第二行,逻辑上我们打印的本应该是Glyph.draw(),然而它被子类覆盖了。而且,就算打印的是RoundGlyph.draw(),radius=0,那radius应该等于1才对啊!不是的,按照类的初始化顺序,先初始化的是父类Glyph的构造器,然后才轮到初始化RoundGlyph的成员变量radius。之所以radius=0,是因为Java类的加载机制的准备阶段,即在用户初始化变量之前,就已经为变量初始化为0了,关于Java虚拟机的类加载机制,可以看下《深入理解Java虚拟机》一书的第7章。

      这种bug很难查找,但是又会破坏程序本身,让我们忘bug兴叹。

      所以我们在对构造器进行初始化的时候,要尽量简单,尽量避免在构造方法内调用其他public的非构造方法(private方法可以调用,因为它不可被继承)。

  • 相关阅读:
    Java Number类
    Java StringBuffer和StringBuilder类
    Java String类
    Java 日期时间
    Java 数组
    Java 方法
    Java正则表达式
    一步步学习SPD2010--第五章节--处理数据视图(6)--使用XSLT
    一步步学习SPD2010--第五章节--处理数据视图(5)--显示和隐藏内容
    一步步学习SPD2010--第五章节--处理数据视图(4)--应用条件格式
  • 原文地址:https://www.cnblogs.com/darkhorse-pxf/p/4708597.html
Copyright © 2011-2022 走看看