Think inJava学习笔记 第七章 复用类
当创建了一个导出类的对象时,该对象包含了一个基类的子对象。这个子对象与你用基类直接创建的对象是一样的。二者的区别在于,后者来自外部,而基类的子对象被包装在导出类对象内部。
当然,对基类子对象的正确初始化也是至关重要的,而且也仅有一种方法来保证这一点:在构造器中调用基类构造器来执行初始化,而基类构造器中具有执行基类初始化所需要的所有知识和能力。Java自动在导出类的构造器中插入对基类构造器的调用。
因为子类构造器在执行的时候会依次递归把其父类的构造器都执行一遍,所以我们可以在子类中获取父类的方法、属性。有时候我们使用super
,这个super
指代的就是父类的对象。
当然也有不能自动执行父类构造器的时候:
class A {
A(int a) {
}
}
class B extends A{
}
如上述代码所示,A类有一个含参数的构造器,这个构造器会替代原有的默认构造器。所以B类就不能够使用默认构造器了,必须手动调用A的参数构造器。因为B类要调用A的构造器,但是A只有一个含参数的构造器,这个时候B不知道怎么向A的构造器传递参数。所以编译器会报如下错误警告:
Implicit super constructor A() is undefined for default constructor. Must define an explicit constructor
这个时候就需要我们手动为A类传入参数就行啦。
class A {
A(int a) {
}
}
class B extends A{
B() {
super(1);
}
}
虽然编译器强制你去初始化基类,并且要求你要在构造器起始处就这么做,但是它并不监督你必须将成员对象也初始化,因此在这一点上你自己必须时刻注意。
成员对象会在我们创建该类的对象的时候赋予一个值为“0”的初始值,所以我们不用担心类似于空指针的异常。而如果不先初始化父类的话,就不能够调用父类的方法和变量了。
如果Java基类拥有某个已经被多次重载的方法名称,那么在导出类中重新定义该方法名称并不会屏蔽其在基类中的任何版本。
正如之前所提到的,一个方法是由其方法名和参数列表所决定的,子类中如果出现了与父类方法名或者形参列表不同的方法,那这就是子类独有的方法,和父类的方法没有任何关系,也就不会覆盖掉父类的方法。
Java允许生成“空白
final
”,所谓空白final
是指被声明为final但又未给定初始值的参数。无论什么情况下,编译器确保空白final
在使用前都必须被初始化。但是空白final
在关键字final
上提供了更大的灵活性。
final关键字在使用前必须被初始化,那么我们可以在构造器、初始化块中对其初始化。
类中所有的
private
方法都隐式的指定为final。由于无法使用private
方法,所以也就爱无法覆盖它。
我们知道final
修饰一个方法是为了让它不被子类重写。如果我们在子类中创建一个方法名和形参列表都和父类方法一致的方法的话,编译器就会报错。但是如果使用private
而不是final
修饰父类方法的话,子类则不会报错。
class A {
A(int a) {
}
private final void test() {
}
}
class B extends A{
B() {
super(1);
}
void test(int i){
}
}
因为这个时候在子类看来,父类并没有同名的方法,父类中的private
方法对子类是不可见的。举个例子,公司A发明了一个新配方,但是不想申请专利,因为专利年限就几十年,公司A认为这个配方能带来几百年的利益。然后公司B后来也发明了这个相似的配方,公司A就不能禁止公司B使用这个配方了,因为B完全是自己发明的,和A一点关系都没有的。