Java教程

【JAVA】 重用类-合成&继承

本文主要是介绍【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)关系是用合成来表达的。

这篇关于【JAVA】 重用类-合成&继承的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!