第7章 复用类
7.2 继承语法
如果基类没有默认构造函数,那么必须在Derive类的构造函数中显示调用Base类的带参构造函数。
1 import static java.lang.System.out; 2 class Base { 3 Base(String name){ 4 out.println("Base constructor"); 5 } 6 } 7 8 public class Derive extends Base { 9 Derive(){ 10 super("test"); /* If comment out this line, result a compile error*/ 11 } 12 public static void main(String args[]){ 13 } 14 }
如果基类具有默认构造函数,那么编译器会帮你在Derive类的构造函数中黯然插入对于基类默认构造函数的掉用。
1 import static java.lang.System.out; 2 class Base { 3 Base(){ 4 out.println("Base constructor"); 5 } 6 Base(String name){ 7 out.println("Base constructor with one parameter"); 8 } 9 } 10 11 public class Derive extends Base { 12 Derive(){ 13 //super(); /*Compilor inserts this line for you implicity*/ 14 } 15 Derive(String name){ 16 //super(); /*Compilor inserts this line for you implicity*/ 17 } 18 public static void main(String args[]){ 19 new Derive(); 20 new Derive("name"); 21 } 22 }/* Output: 23 Base constructor 24 Base constructor 25 */
7.4 结合使用组合和继承
7.4.2 名称屏蔽
C++中重载只可以发生在水平层面上(即同一个类中,同一个作用域中),英文记做overloading。而一旦这种overloading的表现出现在垂直层面上(即继承体系上,一般是Derive类中声明一个方法的名称与Base类中的某个方法相同),英文记做overriding,也就是说overloading在垂直层面上不存在。而Derive类可以通过override一个在Base类中被声明为virtual的方法(这个方法在Base类和Derive类中的方法签名必须完全相同,亦即function name+parameter list必须完全相同,但return type可以是Base类方法return type的子类。详细参见这里:http://stackoverflow.com/questions/4665117/c-virtual-function-return-type)来实现runtime polymorphism。当overloading出现在处置层面的时候,并且方法没有被标记为virtual,那么父类的方法就被子类覆盖了,记住:被覆盖的不是某一个具体的方法,而是具有这个方法签名的所有方法。
Java中所有的方法默认都是Runtime绑定的,Java中的Overloading可以发生在不同层面上(Derive类中定义的方法与Base类中的相同签名的方法构成overloading),Java中可以用@Override 让Compiler帮助你正确的Override一个方法(你可能错误的进行了overloading)
1 import static java.lang.System.out; 2 class Base { 3 void f(){} 4 } 5 public class Test extends Base { 6 @Override 7 void f(int i){} /*Result a compile errror unless remove parameter declaration*/ 8 public static void main(String args[]){ 9 } 10 }
7.8 final关键字
7.8.1 final数据
1 public class Test{ 2 private final int a = 1; 3 private final int b; //Blank final 4 { 5 //a = 2; //final data can not reassign 6 b = 4; 7 } 8 9 public Test(){ 10 //a = 3; //final data can not reassign 11 } 12 public static void main(String args[]){ 13 new Test(); 14 } 15 }
或者
1 public class Test{ 2 private final int a = 1; 3 private final int b; //Blank final 4 5 public Test(){ 6 //a = 3; //final data can not reassign 7 b = 4; 8 } 9 public static void main(String args[]){ 10 new Test(); 11 } 12 }
final数据也可以是local的哟~
1 public class Test{ 2 public static void main(String args[]){ 3 final int i; 4 i = 3; 5 //i = 5; /* This result a compile error */ 6 } 7 }
final基本类型的数据值不可变,final对象类型的数据引用不可变对象本身可变,Java在语法上不提供对象本身不可变的方法。
final参数与C++中的const reference parameter有着相同的效果:你不可以更改引用的对象,但被引用对象的内容的修改不受此关键字的限制。
7.8.2 final方法
final方法阻止Derive类修改它的含义,也就是组织了多态。
Java5之前final方法还对编译器施加了类似于C++中inline的作用。但现在已经不再提倡。
private的方法意义上等同与final,但对于Derive类override的限制上有着不同的行为,详细看如下代码。
1 class Base { 2 private void f1() {} 3 private final void f2() {} 4 public final void g() {} 5 } 6 7 public class Derive extends Base { 8 void f1() {} 9 void f2() {} 10 /* This result a compile error : g() in Derive cannot override g() in Base, for overridden method is final*/ 11 void g() {} 12 public static void main(String args[]){} 13 }
7.8.3 final类
语法上阻止了被声明为final的类被他人extends。
让我们来回忆一下如何在C++中实现这种效果,注意C++的语法:class中默认访问权限为private。
1 class UnDerived { 2 UnDerived(){} 3 }; 4 5 class Derive : UnDerived { 6 }; 7 8 int main() 9 { 10 Derive d; 11 return 0; 12 }/* Compile output: 13 main.cpp: In constructor 'Derive::Derive()': 14 main.cpp:2:5: error: 'UnDerived::UnDerived()' is private 15 main.cpp:5:7: error: within this context 16 main.cpp: In function 'int main()': 17 main.cpp:10:11: note: synthesized method 'Derive::Derive()' first required here 18 */
7.9 初始化及类的加载
类的加载顺序为,先遇到先加载,某个类如果有基类,那么它的基类也会被加载。
1 import static java.lang.System.out; 2 class Indicator { 3 Indicator(String name){ 4 out.println(name); 5 } 6 } 7 8 class Base { 9 static Indicator i1 = new Indicator("i1"); 10 static Indicator i2; 11 static { 12 i2 = new Indicator("i2"); 13 } 14 Base(){} 15 } 16 17 public class Derive extends Base { 18 static Indicator i6 = new Indicator("i6"); 19 static Indicator i7; 20 static { 21 i7 = new Indicator("i7"); 22 } 23 Derive(){} 24 public static void main(String args[]){ 25 } 26 }/* 27 i1 28 i2 29 i6 30 i7 31 */
构造函数以及Static域的初始化顺序为先基类后子类。
确切的描述为:
- 加载子类,加载基类,加载根基类
- 根基类的static初始化,基类,最后是子类
- 根基类的构造函数被调用,基类,最后是子类
1 import static java.lang.System.out; 2 class Indicator { 3 Indicator(String name){ 4 out.println(name); 5 } 6 } 7 8 class Base { 9 static Indicator i1 = new Indicator("i1"); 10 static Indicator i2; 11 static { 12 i2 = new Indicator("i2"); 13 } 14 private Indicator i3 = new Indicator("i3"); 15 private Indicator i4; 16 private Indicator i5; 17 { 18 i4 = new Indicator("i4"); 19 } 20 21 Base(){ 22 i5 = new Indicator("i5"); 23 } 24 } 25 26 public class Derive extends Base { 27 static Indicator i6 = new Indicator("i6"); 28 static Indicator i7; 29 static { 30 i7 = new Indicator("i7"); 31 } 32 private Indicator i8 = new Indicator("i8"); 33 private Indicator i9; 34 private Indicator ia; 35 { 36 i9 = new Indicator("i9"); 37 } 38 Derive(){ 39 ia = new Indicator("ia"); 40 } 41 public static void main(String args[]){ 42 new Derive(); 43 } 44 }/* 45 i1 46 i2 47 i6 48 i7 49 i3 50 i4 51 i5 52 i8 53 i9 54 ia 55 */