美文网首页
重复运用Class

重复运用Class

作者: 刚子来简书啦 | 来源:发表于2020-09-23 11:04 被阅读0次
    组合与继承

    在新的class中产生既有class的对象,这种方法称为组合(composition)。这种情形只是很单纯地重复运用既有程序代码的功能,而非重复运用其形式。而继承则既可以接收既有class的形式,并加入新代码,也无需更动既有的class。

    Java编译器会自动在derived class构造函数中插入对base class的构造函数的调用动作。如果base class不具备default构造函数(无引数列表),或如果就是想调用带有引数的base class构造函数,那么就得运用super关键字,并搭配适当的引数列,显式地写出调用动作。该调用必须写在构造函数的第一行,也就是这必须是构造函数所做的第一件事情。

    关键字final

    不变的数据可能很有用,因为它:

    1. 可以是永不改变的“编译器常量”。
    2. 可以在执行期被初始化,然后不想再被改变。

    在Java中,此类常量必须属于基本型别,而且必须以关键字final修饰之。定义此类常量的同时,必须给定其值。

    如果数据既是static也是final,那么它会拥有一块无法改变的存储空间。

    将final用于基本型别时,final让数值保持不变;但是用于 object reference 时,final 让 reference 保持不变。某个reference一旦被初始化用以代表某个对象后,便再也不能改而指向另一个对象。但此时对象本身的内容却是可以更动的。Java并未提供“让任何对象保持不变”的机制。

    不能只因为某个数据被声明为final,就认为在编译期便知其值。

    编译器强迫你一定得对所有final执行赋值动作,如果不是发生在其定义处,就得在每个构造函数中以表达式设定其值。这也就是为什么能够保证“final数据成员在被使用前绝对会被初始化”的原因。

    Java允许将引数(arguments)声明为final,意味着无法在此函数中令该引数(一个reference)改指它处。

    在函数前使用final的原因有两个:

    1. 锁住这个函数,使 derived class 无法改变其含义。
    2. 效率。如果某个函数被声明为final,等于允许编译器将所有对此函数的调用动作转为行内调用。

    class中的所有private函数自然而然会是final。因为无法取用private函数,自然也就无从覆写。但是你可以在 derived class 中使用同样的函数名,只是此时与覆写没有任何关系。

    初始化和class装载

    class程序代码在初次被使用时才被装载。所谓初次使用,不仅是其第一个对象被构建之时,也可能是在某个static数据成员或static函数被取用时。

    “首次使用class”的时间点,也正是static初始化的进行时机,任何static对象和static程序区段被装载时,都会依据它们在程序代码中的次序加以初始化。当然,static只会被初始化一次。

    // base class
    class Insect {
    
        int i = 1;
        int j;
    
        Insect() {
            prt("i = " + i + ", j = " + j);
            j = 8;
        }
    
        static int x1 = prt("static Insect.x1 initialized");
    
        static int prt(String s) {
            System.out.println(s);
            return 27;
        }
    }
    
    // derived class
    class Beetle extends Insect {
    
        int k = prt("Beetle.k initialized");
    
        Beetle() {
            prt("k = " + k);
            prt("j = " + j);
        }
    
        static int x2 = prt("static Beetle.x2 initialized");
    
        public static void main(String[] args) {
            prt("Beetle constructor");
            Beetle beetle = new Beetle();
        }
    }
    

    这个程序的运行结果如下:

    static Insect.x1 initialized
    static Beetle.x2 initialized
    Beetle constructor
    i = 1, j = 0
    Beetle.k initialized
    k = 27
    j = 8
    

    当执行Beetle时,首先发生的动作便是取用Beetle.main(),于是装载器被启动,找出Beetle class编译过的程序代码(Beetle.class文件)。装载过程中由于关键字extends存在,转载器得知这个class拥有base class,于是继续装载。无论你是否产生base class的对象,这个动作都会发生。

    如果base class还有base class,那么便会继续装载第二个base class,依此类推。接下来,root base class中的静态初始化动作会被执行,然后是其derived class,依此类推。上述方式很重要,因为derived class的静态初始化动作是否正确,可能和base class的成员是否被正确初始化有关。

    至此,所有必要的class都已被装载,可以开始产生对象了。首先,对象内的所有基本型别都会被设置缺省值,object reference则被设为null,也就是将对象内存都设为二进位零值。然后,base class的构造函数会被唤起。base class的构建过程和其derived class构造函数中的次序相同。base class构造函数完成之后,其实例变量会以其出现次序被初始化。最后才执行构造函数本体的剩余部分。

    相关文章

      网友评论

          本文标题:重复运用Class

          本文链接:https://www.haomeiwen.com/subject/pbpmphtx.html