复用类
组合语法
假设你需要创建一个具有多个基本数据类型,多个String对象,以及另一个类的对象。对于基本类型你可以直接定义,但对于非基本类型的对象,必须将其引用置于新的类中。
class WaterSoure{
private String s;
WaterSource(){
System.out.println(“WaterSource()”);
s=”Constructed”;
}
public String toString(){return s;}
}
public class SprinklerSystem{
private String value1,value2,value3,value4;
private WaterSource source=new WaterSource();
private int i;
private float f;
public String toString(){
return
“value1 =”+value1+” ”+
“value2 =”+value2+” ”+
“value3 =”+value3+” ”+
“value4 =”+value4+” ”+
“i =” +i+” “+”f=”+ f +” “+
“source = “ + source;
}
public static void main(String[] args){
SprinklerSystem sprinklers=new SprinklerSystem();
System.out.println(sprinklers);
}
}
输出
WaterSource();
value1 = null value2 = null value3 = null value4 = null
i=0 f=0.0 source=Constructed
在上面两个类中的方法中,有一个特殊的方法叫做toString().每一个非基本类型的对象都具有一个toString()方法。这个方法会在系统需要一个String对象的时候,而你却只有一个对象时被调用。所以在表达式中:
“source =”+source;
系统知道你此时需要一个String对象,因为将一个String和另一个String相加,因此编译器将调用toString()方法,来吧source对象转化成一个String。因此每当你想创建的类需要具备此类功能的时候只需要编写toString()方法即可。
上面的例子可以看出编译器自动为没有初始化的对象,赋予了默认值null.
但编译器并不是简单的为每一个对象都创建默认对象,这样做的目的是为了个程序减少负担。如果想要初始化这些对象可以在代码以下位置进行。
1,定义对象的地方,这意味着他们总是能够在构造器被调用之前被初始化。
2,在类的构造器中。
3,在使用对象之前(惰性初始化)。
4,使用实例初始化。
class Soap{
private String s;
Soap(){
System.out.println(“Soap()”);
s= “Constructed”;
}
public String toString(){return s;}
}
public class
private String //定义时初始化
s1= “Happy”,
s2=”Happy”,
s3,s4;
private Soap
private int i;
private float toy;
public
System.out.println(“Inside
s3=”Joy”;
toy=3.14f;
}
{i =47;}//实力初始化
public String toString(){
if(s4==null) //使用之前初始化
s4=”Joy”
return
“s1 =”+s1+”\n”+
“s2 =”+s2+”\n”+
“s3 =”+s3+”\n”+
“s4 =”+s4+”\n”+
“i =” +i+” “+”toy=”+ toy+” “+
“castille = “ + castille;
}
public static void main(String[] args){
System.out.println(b);
}
}
输出
Insid Bath();
Soap();
s1=Happy
s2=Happy
s3=Joy
s4=Joy
i=47
toy=3.14
继承语法
继承是所有面向对象语言所不可或缺的部分。当创建一个类时,总是在继承(Object类)。
不同于组合的语法,继承使用的是一种特殊的语法,在继承工程中,需要先生命“新类与旧类相似”、这种声明是通过在类主题左边的花括号之前,书写后面紧随基类名称关键字extends而实现的。当这么做时,会自动得到积累中所有的域和方法。例如:
class Cleanser{
private String s=”Cleanser”;
public void append(String a){s+=a;}
public void dilute(){append(“ dilute()”);}
public void apply(){append(“ apply()”);}
public void scrub(){append(“ scrub()”);}
public String toString(){return s;}
public static void main(String[] args){
Cleanser x=new Cleanser();
System.out.println(x);
}
}
public class Detergent extends Cleanser{
public void scrub(){
append(“ Detergent.scrub()”);
super.scrub();
}
public void foam() { append(“ foam()”);}
public static void main(String[] args){
Detergent x=new Detergent();
x.dilute();
x.apply();
x.scrub();
x.foam();
System.out.println(“Testing base class:”);
Cleanser.main(args);
}
}
输出
Cleanser dilute() apply() Detergent.scrub() scrub() foam()
Cleanser dilute() apply() scrub()
这里使用了Java的许多新特性
1, 使用+=对字符串进行连接处理
2, 上面两个类均有main方法。可在每个类中都创建main方法,这样做可以让单元测试变得简单易行,此外即使一个程序中多个类也只有命令行调用的那个类的main()方法会被调用,此例中调用的java Detergent.而我们在Detergent.main()中调用了Cleanser.main().并将命令行获取的参数传给了它。当然,也可以传递任意的String数组。
3, 继承一个类,只能访问被继承类的public方法。如果不加访问修饰符,那么默认的访问权限是包访问权限,只可以包内成员访问。因此为了继承一般将所有的字段(数据成员)都设置成private,将所有的方法都设置成public.
4, Cleanser中的一些方法,虽然在Detergent没有显示的定义,但是,由于Detergent是由关键字extends从Cleanser导出的,因此Detergent自动获得了这些方法。所以可以将继承视作对类的复用。
5, 我们在子类中修改了scrub方法,可能你的修改需要调用父类中的方法,但是在scrub()中不能直接调用scrub(),因为这样做会产生递归,为解决此问题,Java用关键字super表示超类的意思(父类),当前类就是从超类继承来的,为此,表达式super.scrub()将调用父类中scrub()方法
6, 在继承的过程中,也可以在导出类中添加新方法,其添加方法与类中添加任意方法一样。
初始化基类
由于涉及了继承,因此对于导出类从外部开来,他就像是一个与基类具有相同接口的新类,或许还会有一些额外的方法和域。单继承并不只是复制基类的接口,当创建了一个导出类的对象时,该对象包含了一个基类的子对象。这个子对象与你用基类直接创建的对象是一样的。二者区别在于,后者来自于外部,而类的子对象被包装在导出类对象内部。
因此对积累子对象的正确初始化也是至关重要的。Java会在导出类的构造器中插入对基类构造器的调用。
class Art{
Art();
}
class Drawing extends Art{
Drawing(){System.out.println(“Drawing constructor”);}
}
public class Cartoon extends Drawing{
public Cartoon(){System.out.prinln(“Cartoon constructor”);}
public static void main(String[] args){
Cartoon x=new Cartoon();
}
}
输出
Art constructor
Drawing constructor
Cartoon constructor
从上例可以发现构造过程是从基类“向外”扩散的,所以基类在导出类构造器可以访问它之前,就已经完成了初始化。