This post was republished to 黄立 at 15:00:57 2010-5-18
Chapter 6 Reusing Classes
Category Thinking in java note
Inheritance syntax
Syntax
使用extend即可。
Initializing the base class
Testing code:
class Art {
Art() {
System.out.println("Art constructor");
}
}
class Drawing extends Art {
Drawing() {
System.out.println("Drawing constructor");
}
}
public class Cartoon extends Drawing {
public Cartoon() {
System.out.println("Cartoon constructor");
}
public static void main(String[] args) {
Cartoon x = new Cartoon();
}
} ///:
Result:
Art constructor
Drawing constructor
Cartoon constructor
Conclusion: base class is initialized before the derived-class constructors can access it.先初始化父类,保证了不会产生垃圾变量。
Combining composition and inheritance
Name hiding
Testing code:
class Homer {
char doh(char c) {
System.out.println("doh(char)");
return 'd';
}
float doh(float f) {
System.out.println("doh(float)");
return 1.0f;
}
}
class Milhouse {}
class Bart extends Homer {
void doh(Milhouse m) {
System.out.println("doh(Milhouse)");
}
/*char doh(char c) {
System.out.println("doh(overload char)");
return 'd';
}*/
}
public class Hide {
public static void main(String[] args) {
Bart b = new Bart();
b.doh(1);
b.doh('x');
b.doh(1.0f);
b.doh(new Milhouse());
b.doh(null);
}
} ///:~
Conclusion:即使子类有相同的方法名,只要参数不同(如果参数相同,返回值相同就会覆盖父类的方法)。如果在c中必须写不同的函数,或者通过增加参数,SIZEOF来判断比java麻烦。
Choosing composition vs. inheritance
何时使用组合或者组合是挺复杂的一个问题,需要不断的实践才能慢慢体会。目前我的想法是如果使用了继承那么父类的方法全部暴露出来,父类和子类如果是一个包含的关系可以使用继承,如,Shape包含于rectangle。使用组合的话,父类和子类应该是一个组成的关系,如书中汽车的例子。如同书中说的Is-a还是Has-a的关系
Protected
Testing code:
import java.util.*;
class Villain {
private String name;
protected void set(String nm) { name = nm; }
public Villain(String name) { this.name = name; }
public String toString() {
return "I'm a Villain and my name is " + name;
}
}
public class Orc extends Villain {
private int orcNumber;
public Orc(String name, int orcNumber) {
super(name);
this.orcNumber = orcNumber;
}
public void change(String name, int orcNumber) {
set(name); // Available because it's protected
this.orcNumber = orcNumber;
}
public String toString() {
return "Orc " + orcNumber + ": " + super.toString();
}
public static void main(String[] args) {
Orc orc = new Orc("Limburger", 12);
System.out.println(orc);
orc.change("Bob", 19);
System.out.println(orc);
}
} ///:~
Conclusion:子类可以访问父类中的protected方法。注意return那行,代码非常简洁。
Incremental development
One of the advantages of inheritance is that it supports incremental development. You can introduce new code without causing bugs in existing code; 的确如果是C代码没有继承,如何新的开发bug都比较难找。
Upcasting
Testing code:
import java.util.*;
class Instrument {
public void play() {}
static void tune(Instrument i) {
// ...
i.play();
}
}
// Wind objects are instruments
// because they have the same interface:
public class Wind extends Instrument {
public static void main(String[] args) {
Wind flute = new Wind();
Instrument.tune(flute); // Upcasting
}
} ///:~
我对Upcasting的理解是最大可能的增加复用,减少重复敲代码。
- Design
- Efficiency
Three places where final can be used
- Data
- Methods
- Classes
Final有点类似于c中的constant,对于一个对象的话final的使用相当于固定对象,但是对象的值可以变。像结婚一样,我只和你结婚,结婚后你怎么变无所谓。C中对应的好像指针常量(char * const)。
import java.util.*;
class Value {
int i; // Package access
public Value(int i) { this.i = i; }
}
public class FinalData {
private static Random rand = new Random();
private String id;
public FinalData(String id) { this.id = id; }
// Can be compile-time constants:
private final int VAL_ONE = 9;
private static final int VAL_TWO = 99;
// Typical public constant:
public static final int VAL_THREE = 39;
// Cannot be compile-time constants:
private final int i4 = rand.nextInt(20);
static final int i5 = rand.nextInt(20);
private Value v1 = new Value(11);
private final Value v2 = new Value(22);
private static final Value v3 = new Value(33);
// Arrays:
private final int[] a = { 1, 2, 3, 4, 5, 6 };
public String toString() {
return id + ": " + "i4 = " + i4 + ", i5 = " + i5
+ " a[0] = " + a[0] + ", a[1] = " + a[1];
}
public static void main(String[] args) {
FinalData fd1 = new FinalData("fd1");
//! fd1.VAL_ONE++; // Error: can't change value
fd1.v2.i++; // Object isn't constant!
fd1.v1 = new Value(9); // OK -- not final
for(int i = 0; i < fd1.a.length; i++)
fd1.a[i]++; // Object isn't constant!
//! fd1.v2 = new Value(0); // Error: Can't
//! fd1.v3 = new Value(1); // change reference
//! fd1.a = new int[3];
System.out.println(fd1);
System.out.println("Creating new FinalData");
FinalData fd2 = new FinalData("fd2");
System.out.println(fd1);
System.out.println(fd2);
}
} ///:~
Results:
fd1: i4 = 10, i5 = 10 a[0] = 2, a[1] = 3
Creating new FinalData
fd1: i4 = 10, i5 = 10 a[0] = 2, a[1] = 3
fd2: i4 = 9, i5 = 10 a[0] = 1, a[1] = 2
这个例子清晰的演示了object和primitive type 中final的区别。注意数组也是一个object的引用。
Blank final
Testing code:
class Poppet {
private int i;
Poppet(int ii) { this.i = ii; }
}
public class BlankFinal {
private final int i = 0; // Initialized final
private final int j; // Blank final
private final Poppet p; // Blank final reference
// Blank finals MUST be initialized in the constructor:
public BlankFinal() {
j = 1; // Initialize blank final
p = new Poppet(1); // Initialize blank final reference
}
public BlankFinal(int x) {
j = x; // Initialize blank final
p = new Poppet(x); // Initialize blank final reference
}
public static void main(String[] args) {
new BlankFinal();
new BlankFinal(47);
System.out.println();
}
}
Conclusion:如果final没有在定义时初始化,就必须在构造函数中初始化。把BlankFinal中对对象Poppet 的初始化注释掉会报错。
Final methods
Two reasons for final methods:
- first is to put a "lock" on the method to prevent any inheriting class from changing its meaning.
- The second reason for final methods is efficiency
如果父类中的方法不想让子类继承,父类中用private可以达到效果,不过同一个Package下的其他类也不能访问了。
Final还可以用来修饰参数(同C)和class。Class修饰为final则不能继承。
Initialzation with inheritance
class Insect {
private int i = 9;
protected int j;
Insect() {
System.out.println("i = " + i + ", j = " + j);
j = 39;
}
private static int x1 =
print("static Insect.x1 initialized");
static int print(String s) {
System.out.println(s);
return 47;
}
}
public class Beetle extends Insect {
public Beetle() {
System.out.println("k = " + test);
System.out.println("j = " + j);
}
private int k = print("Beetle.k initialized");
private static int x2 =
print("static Beetle.x2 initialized");
public int test = print("Beetle.k initialized");
public static void main(String[] args) {
System.out.println("Beetle constructor");
Beetle b = new Beetle();
}
} ///:~
Result:
static Insect.x1 initialized
static Beetle.x2 initialized
Beetle constructor
i = 9, j = 0
Beetle.k initialized
Beetle.k initialized
k = 47
j = 39
Conslusion: 初始化时先会初始化父类的static变量,然后初始化构造函数,然后按照文本顺序进行变量的初始化。
My thinking in java
Overload:为了增加代码复用的一种方法,同时也降低了代码的负责度。我可以不管参数的变化,只需要调用相同的方法。好比去吃饭,我一个人去吃饭可以同样的点菜,几个人也是同样的点菜,这也相当于是一种重载了。
继承:如果C中想要继承,那就相当于重新copy在修改了。非常不优雅。
java用了Public,protected,private,可以让不必要的接口隐藏掉,看到该看到的。接着有了继承,还好只有单继承,不然是前进10步倒退了7步了,继承的设计我觉得挺难,什么该用protected,什么时候用private得好好思考。为了控制有些方法不能被继承,但是又能被同一Package下的类访问,发明了final methods,为了联系同一类型对象之间的联系,有了static。这些设计看起来还是蛮顺其自然的。