【JAVA】 重用类-合成&继承
作者:互联网
合成(Composition)
(hasA)
进行合成只需在新类里简单地置入对象句柄即可。
//: SprinklerSystem.java
// Composition for code reuse
package c06;
class WaterSource {
private String s;
WaterSource() {
System.out.println("WaterSource()");
s = new String("Constructed");
}
public String toString() { return s; }
}
public class SprinklerSystem {
private String valve1, valve2, valve3, valve4;
WaterSource source;
int i;
float f;
void print() {
System.out.println("valve1 = " + valve1);
System.out.println("valve2 = " + valve2);
System.out.println("valve3 = " + valve3);
System.out.println("valve4 = " + valve4);
System.out.println("i = " + i);
System.out.println("f = " + f);
System.out.println("source = " + source);
}
public static void main(String[] args) {
SprinklerSystem x = new SprinklerSystem();
x.print();
}
} ///:~
/* Output:
WaterSource()
valve1 = null
valve2 = null
valve3 = null
valve4 = null
i = 0 f = 0.0 source = Constructed
*///:
注意toString()方法,每种非基本类型的对象都有一个toString()方法。若编译器本来希望一个String,但却获得某个对象,就会调用这个方法。所以在下面这个表达式中:
System.out.println("source = " + source) ;
编译器会发现我们试图向一个WaterSource添加一个String对象(“source =”)。这对它来说是不可接受的,因为只能将一个字串“添加”到另一个字串,所以它会说:“我要调用toString(),把source转换成字串!”经这样处理后,它就能编译两个字串,并将结果字串传递给System.out.println()。
要在自己创建的类中允许上述这种行为,只需要写一个toString()方法即可。
注意对象句柄会初始化为null,调用其方法会抛出违例
继承(inheritance)
子类继承父类(isA)
语法:访问控制符【修饰符】class 类名 extends 父类名{}
关键字:extends
例如: public class Students extends Person{}
//: Detergent.java
// Inheritance syntax & properties
class Cleanser {
private String s = new String("Cleanser");
public void append(String a) { s += a; }
public void dilute() { append(" dilute()"); }
public void apply() { append(" apply()"); }
public void scrub() { append(" scrub()"); }
public void print() { System.out.println(s); }
public static void main(String[] args) {
Cleanser x = new Cleanser();
x.dilute();
x.apply();
x.scrub();
x.print();
}
}
public class Detergent extends Cleanser {
// Change a method:
public void scrub() {
append(" Detergent.scrub()");
super.scrub(); // Call base-class version
}
// Add methods to the interface:
public void foam() { append(" foam()"); }
// Test the new class:
public static void main(String[] args) {
Detergent x = new Detergent();
x.dilute();
x.apply();
x.scrub();
x.foam();
x.print();
System.out.println("Testing base class:");
Cleanser.main(args);
}
}
/* Output:
Cleanser dilute() apply() Detergent.scrub() scrub() foam()
Testing base class:
Cleanser dilute() apply() scrub()
///:~
注意:
无论Cleanser还是Detergent都包含了一个main()方法。我们可为自己的每个类都创建一个main()。通常建议大家这样编写代码,使自己的测试代码能够封装到类中。即便在程序中含有数量众多的类,但对于在命令行请求的public类,只有main()才会得到调用。所以在这种情况下,当我们使用“java Detergent”的时候,调用的是Degergent.main()——即使Cleanser并非一个public类。采用这种将main()置入每个类的做法,可方便地为每个类都进行单元测试。而且在完成测试以后,毋需将main()删去;可把它保留下来,用于以后的测试。
使用Deteregent.main()调用主方法
初始化基础类
基础类(父类)和衍生类(子类)。从外部看,新类似乎与基础类拥有相同的接口,可能还包含一些额外添加的方法和字段。但继承不是简单复制基础类的接口就完了。创建衍生类的一个对象时,它在其中包含了基础类的一个“子对象”。这个子对象就象我们根据基础类本身创建了它的一个对象。从外部看,基础类的子对象已封装到衍生类的对象里了。
基础类子对象要正确地初始化,就只能在构建器中执行初始化。在衍生类的构建器中,Java会自动插入对基础类构建器的调用。见下例:
//: Cartoon.java
// Constructor calls during inheritance
class Art {
Art() {
System.out.println("Art constructor");
}
}
class Drawing extends Art {
Drawing() {
System.out.println("Drawing constructor");
}
}
public class Cartoon extends Drawing {
Cartoon() {
System.out.println("Cartoon constructor");
}
public static void main(String[] args) {
Cartoon x = new Cartoon();
}
} ///:~
output:
Art constructor
Drawing constructor
Cartoon constructor
可以看到,构建是在基础类的“外部”进行的,所以基础类会在衍生类访问它之前得到正确的初始化。
初始化子类时会首先调用父类的构造器,初始化父类
如果父类没有默认的自变量,或是想调用某个有自变量的父类的构造器,则必须明确的写出对父类的调用代码。这时,需要用super关键字以及适当的自变量列表实现。见下例:
//: Chess.java
// Inheritance, constructors and arguments
class Game {
Game(int i) {
System.out.println("Game constructor");
}
}
class BoardGame extends Game {
BoardGame(int i) {
super(i);
System.out.println("BoardGame constructor");
}
}
public class Chess extends BoardGame {
Chess() {
super(11);
System.out.println("Chess constructor");
}
public static void main(String[] args) {
Chess x = new Chess();
}
} ///:~
到底选择合成还是继承
无论合成还是继承,都可以将子对象置于新类中。
如果想利用新类内部一个现有类的特性,而不想使用它的接口,通常应选择合成。也就是说,可以嵌入一个对象,能够使用它实现新类的特性。但新类的用户会看到已定义的接口,而不是嵌入对象的接口。考虑到这种效果,我们需在新类里嵌入现有类的private对象。
有些时候,我们想让类用户直接访问新类的合成。也就是说,需要将成员对象的属性变为public。成员对象会将自身隐藏起来,所以这是一种安全的做法。而且在用户知道我们准备合成一系列组件时,接口就更容易理解。
如下例:
//: Car.java
// Composition with public objects
class Engine {
public void start() {}
public void rev() {}
public void stop() {}
}
class Wheel {
public void inflate(int psi) {}
}
class Window {
public void rollup() {}
public void rolldown() {}
}
class Door {
public Window window = new Window();
public void open() {}
public void close() {}
}
public class Car {
public Engine engine = new Engine();
public Wheel[] wheel = new Wheel[4];
public Door left = new Door(),
right = new Door(); // 2-door
Car() {
for(int i = 0; i < 4; i++)
wheel[i] = new Wheel();
}
public static void main(String[] args) {
Car car = new Car();
car.left.window.rollup();
car.wheel[0].inflate(72);
}
} ///:~
如果选择是引用继承,则是使用一个现成的类,制造出这个类的一个特殊版本。意思就是使用一个常规用途的类,并根据特定的需求对其进行定制。只要稍微思考下就知道自己不能用一个车辆对象来合成一辆汽车——汽车并不“包含”车辆;相反,它“属于”车辆的一种类别。“属于”(is a)关系是用继承来表达的,而“包含”(has a)关系是用合成来表达的。
标签:System,JAVA,void,重用,public,合成,println,class,out 来源: https://blog.csdn.net/loyd3/article/details/106448253