复用代码是Java众多引人注目的功能之一。但要想成为为极具革命性的语言,仅仅能够复制代码并对之加以改变是不够的,它还必须能够做更多的事情。
Java中有2种方法使用类而不破坏现有程序代码,方法如下。
第一种:只需在新类中产生现有类的对象。由于新类是由现有类的对象组成,所以这种方法称为组合。
第二种:按照现有类的类型来创建新类。无需改变现有类的形式,采用现有类的形式并在其中添加新代码。这种方式称为继承。
1.组合语法
只需将其引用置于新类中即可。
class WaterSource{
private String s;
WaterSource(){
System.out.println("WaterSource()");
s="Constructed";
}
public String toString(){return s;}
}
public class Test{
private String v1,v2;
private WaterSource source=new WaterSource();
private int i;
public String toString(){
return "v1:"+v1+"v2:"+v2+"i:"+i+"source:"+source;
}
public static void main(String[] args){
Test t=new Test();
System.out,println(t);
}
}
输出:WaterSouce()
v1:null v2:null i:0 source=Constructed
类中域为基本类型的时候能够被自动初始化为零,但是对象引用会被初始化为null。如果想要初始化对象引用,可以在代码中下列位置进行:
- 在定义对象的地方。这意味着它们总是能够在构造器被调用之前被初始化。
- 在类的构造器中。
- 在正要使用这些对象之前,这种方式叫惰性初始化。在生成对象不值得及不必要每次都生成对象的情况下,这种方式可以减少额外的负担。
- 使用实例初始化。
2.继承语法
通过在类主体的左边花括号之前,书写后面紧随基类的名称的关键字extends而实现的。
1.初始化基类
构建过程是从基类“向外”扩散的。
class Art{
Art(){print("Art()");}
}
class Drawing extends Art{
Drawing(){print("Drawing()");}
}
public class Cartoon extends Drawing{
public Cartoon(){print("Cartoon()");}
public static void main(String[] args){
Cartoon a=new Cartoon();
}
}/*Output:
Art()
Drawing()
Cartoon()
*///:~
要是带参数的构造器怎么办呢?必须用super关键字显式的调用基类构造器的语句,并且配以适当的参数列表。super关键字必须放在前面,调用基类构造器必须是你在导出类构造器中要做的第一件事。(区别:调用基类的除构造器外所有的方法所用的super关键可以在任意地方)。
class art{
art(int i){print("art()");}
}
class drawing extends art{
drawing(int i){
super(i);
print("drawing()");
}
public static void main(String[] args){
drawing d=new drawing();
}
}/*Output:
art()
drawing()
*///:~
3.代理
class SpaceShip{
void up(int i){}
void down(int i){}
}
public class Delegation{
private String name;
private SpaceShip con=new SpaceShip();
public void up(int i){
con.up(i);
}
public void down(int i){
con.down(i);
}
}
4.结合使用组合和继承
实例略。
确保正确清理:
可以重写dispose方法来做一些必要的清理工作。清理最好的办法是除了内存以外,不能依赖垃圾回收器去做任何事。如果需要进行清理,最好是编写自己的清理方法,但不要使用finalize()。
名称屏蔽:
如果Java的基类拥有某个已经被多次重载的方法名称,那么在导出类中重新定义该方法名称并不会屏蔽其在基类中的任何版本。(这一点与C++不同)
class Homer{
char do(char c){print("do(char)");}
float do(float f){print("do(float)");}
}
class mile{}
class bart extends homer{
void do(mile m){print("do(mile)");}
}
public class Hide{
public static void main(String[] args){
bart b=new bart();
b.do(1);
b.do('c');
b.do(new mile());
}
}/*Output:
do(float)
do(char)
do(mile)
*///:~
Java SE5中多了@Override注解,它可以保证覆写此方法,而不是重载。
5.向上转型
class Instrument{
public void play(){}
static void tune(Instument i){
i.play();
}
}
public class Wind extends Instrument{
public static void main(String[] args){
Wind f=new Wind();
Instrument.tune(f);
}
}///:~
将Wind引用转换为Instrument引用的动作,我们称之为
向上转型。
6.final关键字
final关键字三种情况:数据、方法和类。
final数据:
使用情况:
- 一个永不改变的编译时常量。
- 一个在运行时被初始化的值,而你不希望它被改变。
基本数据类型,并且以关键字final表示。在对这个常量进行定义的时候,必须对其进行赋值(构造器中赋值也可以,这种final叫做空白final,但是不赋值会报错)。
一个既是static又是final的域只占据一段不能改变的存储空间。
当对对象而不是基本类型运用final时,final使引用恒定不变,一旦引用被初始化指向一个对象,无法再把它改变指向为另一个对象。然而,对象其自身却是可以被修改的。
final参数情况:无法在方法中修改参数引用所指向的对象。
final方法:
使用final方法原因:
- 把方法锁定,以防任何人继承类修改它的含义。
- 效率。在以前的java实现中会把指定了final的方法当作内嵌函数调用。从而加快程序的执行,但是现在Java SE5/6时,应该让编译器和JVM去处理效率的问题。所以现在一般都是明确禁止覆盖,才将方法设置为final的。
final和private区别:
类中所有的private方法都隐式的指定为final的。但是此问题容易混淆的地方是:如果你试图覆盖一个private方法,似乎是奏效的,而且编译器也不会给出错误信息。
class withfinal{
private void g(){print("withfinal.g()");}
}
class overfinal extends withfinal{
private final void g(){print("overfinal.g()");}
}
public class test{
public static void main(String[] args){
overfinal q=new overfinal();
q.g();
withfinal p=q;
//can't call the method
//p.g();
}
}
final类
final类禁止继承,所以final类中所有方法都是隐式指定为final的,因此无法覆盖他们。
7.初始化及类的加载
执行顺序:初始化基类的static变量,初始化本类的static变量,初始化基类的变量,执行基类的构造函数,初始化本类的变量,执行本类的构造函数。
class Insert{
private int i=9;
protected int j;
Insert(){
print("i="+i+",j="+j);
j=39;
}
private static int x1=printInit("static Insert x1");
static int printInit(String s){
print(s);
return 44;
}
}
public test extends Insert{
private int k=printInit("test k init");
public test(){
print("k="+k);
print("j="+j);
}
private static int x2=printInit("test x2 init");
public static void main(String[] args){
print("test main");
test t=new test();
}
}/*Output:
static Insert x1
static test x2 init
test main
i=9,j=0
test k init
k=44
j=39
*///:~