第七章 复用类
一、组合语法
1、如何使用组合技术?只需将对象引用置于新类中即可。
例如:假设你需要某个对象,它要具有多个string对象,几个基本类型数据,以及另一个类的对象。对于非基本类型的对象,必须将其引用执意要新的类中,但可以直接定义基本类型数据
如下实例:
class WaterSource{
private String string;
WaterSource() {
System.out.println("WaterSource");
string="Constructed";
}
public String toString() {
return string;
}
}
public class SprinkSystem {
//类中域为基本类型时能够自动初始化为零,对象的引用自动初始化为null
private String value1,value2,value3;
private WaterSource waterSource = new WaterSource();
private int i ;
private float f;
public String toString(){
return "value1="+value1+"value2"+value2+"value3"+value3+""+"i="+i+"f"+f+"waterSource"+waterSource;
}
public static void main(String[] args) {
SprinkSystem sprinkSystem = new SprinkSystem();
System.out.println(sprinkSystem);
//每一个非基本类型的对象都有一个toString()方法
//WaterSource waterSource = new WaterSource();
//waterSource.toString();
}
}
/*output
WaterSource
value1=nullvalue2nullvalue3nulli=0f0.0waterSourceConstructed
2、如何初始化对象引用,在代码的那些位置初始化
1)在定义对象的地方,这意味着他们总是能够在构造器被调用之前被初始化
2)在类的构造器中
3)在正要时候用这些对象对象之前,这种方法称为惰性初始化。
4)使用实例初始化
示例如下:
class Soap{//
private String string;
Soap() {
System.out.println("Soap");
string="Constructed";
}
public String toString() {
return string;
}//如果没有tostring()方法,soap将返回这个类的编码,而不会返回Constructed
}
public class Bath {
private String s1="s1",s2="s2",s3,s4;//定义对象地方初始化
private Soap soap;
private int i;
private float f;
private Bath(){
s3="joy";//在类的构造器中初始化
f=3.14f;//在类的构造器中初始化
soap = new Soap();//使用实例初始化
}
{i=2;}//在使用对象之前初始化
public String toString() {
if (s4==null) {
s4="joyu";
}
return "s1="+s1+"s2="+s2+"s3="+s3+"s4="+s4+"i="+i+"f="+f+"soap="+soap;
}
public static void main(String[] args) {
// TODO Auto-generated method stub
Bath bath = new Bath();//实例初始化
System.out.println(bath);
}
}/*output Soap
s1=s1s2=s2s3=joys4=joyui=2f=3.14soap=Constructed
3、
二、代理
使用代理类解决继承所带来的问题:子类继承父类时候将继承父类的所有方法
使用代理类就可以只调用需要的方法
实例如下:
基类:
public class SpaceshipControls {
void up(int velocity){}
void down(int velocity){}
子类:
public class Spaceshipextends SpaceshipControls{
public staticvoid main(String[] args) {
Spaceship spaceship = new Spaceship();
spaceship.up(100);
}
}
代理类:
public class SpaceshipDelegation {
private String nameString;
public SpaceshipDelegation(String nameString) {
this.nameString=nameString;
}
public SpaceshipControlsspaceshipControls =new SpaceshipControls();
public void up(int velocity) {
spaceshipControls.up(velocity);
}
public void down(int velocity) {
spaceshipControls.down(velocity);
}
public staticvoid main(String[] args) {
// TODO Auto-generated method stub
SpaceshipDelegation spaceshipDelegation = new SpaceshipDelegation("spaceshipDelegation");
spaceshipDelegation.up(100);
}
}
三、继承
1、基类初始化:基类在导出构造器在访问它之前就已经完成初始化
2、带参数的构造器:调用带有参数的构造器,就必须使用关键字super显式的编写调用构造器语句,并且配以参数列表
class Cleanser{
public void prt(){//(b)
System.out.println("Cleanser.prt()");
}
}
public class ExplicitStatic extends Cleanser{
public void prt(){
System.out.println("ExplicitStatic.prt()");
}
public static void main(String[] args){
Cleanser x = new ExplicitStatic();
x.prt();//(a)
// The method prt() from the type Cleanser is not visible
}
}
/*output
ExplicitStatic.prt()
四、继承中初始化顺序
初始化顺序为:
1) 加载代码(.class文件)
2) 初始化class的静态成员,初始化顺序了“从里到外”,即从base class开始。
3) 在derived class的构造函数中调用base class的构造函数。
如果在derived class的构造函数中没有通过super()显式调用调用base class的构造函数,编译器会调用bass class的default构造函数并自动生成相应的调用语句,从而产生一个base class实例。如果在derived class的构造函数中通过super()显示调用了父类的构造函数,则调用所指定的构造函数。调用构造函数的调用顺序是“从里到外”。
4) 调用derived class的构造函数。
**:当base class没有default构造函数时,必须在derived class的构造函数中通过super显示调用base class的构造函数。
例:下面代码的初始化过程为:
1) 装载ExplicitStatic的代码(装载ExplicitStatic.class文件)。
2) 发现ExplicitStatic有关键字extends,装载ExplicitStatic的base class的代码(装载Cleanser.class文件)。
3) 发现Cleanser有关键字extends,装载Cleanser的base class的代码(装载Base.class文件)。
4) 初始化Base class中的静态成员。
5) 初始化Cleanser class中的静态成员。
6) 初始化ExplicitStatic class中的静态成员。
如果把(c)处的代码注释掉,那么初始化工作到此就结束了。
7) 为ExplicitStatic对象分配存储空间,并把存储空间初始化为0。
8) 在ExplicitStatic class的构造中调用super("ExplicitStatic")(在ExplicitStatic class的构造函数中显式调用父类的构造函数),试图产生一个Cleanser class实例。
9) 为Cleanser对象分配存储空间,并把存储空间初始化为0。
10) 由于Cleanser class又是继承自Base class,会在Cleanser class的构造函数中通过super()(由于没有显式调用父类的构造函数,所以自动调用父类的default构造函数)调用父类的构造函数,试图产生一个Base class实例。
11) 产生一个Base class实例。先初始化成员变量,再调用构造函数。
12) 回到Cleanser class,产生一个实例。首先初始化Cleanser class中的成员数据,再执行构造函数Cleanser(String a)中的其余部分。
13) 回到ExplicitStatic class,产生一个实例。首先初始化ExplicitStatic class中的成员数据,再执行构造函数ExplicitStatic ()中的其余部分(System.out.println(“ExplicitStatic()”))。
class Base{
static int s1 = prt("s1 initialized.", 11);
int i1 = prt("i1 initialized.", 12);
Base(){
System.out.println("Base()");
System.out.println("s1 = " + s1 + " ,i1 = " + i1);
draw();//(d)
}
void draw(){
System.out.println("base.draw:s1 = " + s1 + " ,i1 = " + i1);
}
static int prt(String s, int num) {
System.out.println(s);
return num;
}
}
class Cleanser extends Base{
static int s2 = prt("s2 initialized.", 21);
int i2 = prt("i2 initialized.", 22);
Cleanser(){
System.out.println("Cleanser()");
System.out.println("s2 = " + s2 + " ,i2 = " + i2);
}
Cleanser(String a){
//super();(b)
System.out.println("Cleanser(" + a + ")");
System.out.println("s2 = " + s2 + " ,i2 = " + i2);
}
void draw(){
System.out.println("Cleanser.draw:s2 = " + s2 + " ,i2 = " + i2);
}
}
public class ExplicitStatic extends Cleanser{
static int s3 = prt("s3 initialized.", 31);
int i3 = prt("i3 initialized", 31);
ExplicitStatic(){
super("ExplicitStatic");//(a)
System.out.println("ExplicitStatic()");
System.out.println("s3 = " + s3 + " ,i3 = " + i3);
}
public static void main(String[] args){
ExplicitStatic x = new ExplicitStatic();//(c)
}
}
/*output
s1 initialized.
s2 initialized.
s3 initialized.
//如果把(c)处的代码注释掉,输出结果到此为止,不会输出下面结果
i1 initialized.
Base()
s1 = 11 ,i1 = 12
Cleanser.draw:s2 = 21 ,i2 = 0//(d)处结果
i2 initialized.
Cleanser(ExplicitStatic)//(a)处结果
s2 = 21 ,i2 = 22
i3 initialized
ExplicitStatic()
s3 = 31 ,i3 = 31
五、Super关键字
1)通过super关键字可以调用的方法是当前父类的方法
实例:
class Base{
Base(){System.out.println("Base()");}
public void scrub() { System.out.println(" Base.scrub()"); }
}
class Cleanser extends Base{
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); }
Cleanser(){
System.out.println("Cleanser(): " + s);
}
public static void testStatic(){
System.out.println("testStatic()");
}
public static void main(String[] args){
Cleanser x = new Cleanser();
x.dilute(); x.apply(); x.scrub(); x.print();
}
}
public class ExplicitStatic extends Cleanser{
ExplicitStatic(){
System.out.println("ExplicitStatic()");
}
public void scrub(){
append(" Detergen.scrub()");
super.testStatic();
super.scrub();//调用的是Cleanser.scrub()
}
public void foam() { append(" foam()"); }
public static void main(String[] args){
ExplicitStatic x = new ExplicitStatic();
x.dilute(); x.apply(); x.scrub(); x.foam();
x.print(); System.out.println("Test base class:");
Cleanser.main(args);
testStatic();
}
}
2)通过super来调用superclass中的成员时,调用的是最近成员。
实例:
class Base{
protected String baseS = "Base";//(a)
//private String baseS = "Base";
Base(){System.out.println("Base()");}
}
class Cleanser extends Base{
protected String baseS = "Cleanser";//(b)
public String s = new String("Cleanser");
Cleanser(){
System.out.println("Cleanser(): " + s);
}
Cleanser(String a){
System.out.println("Cleanser(" + a + "): s = " + s );
}
}
public class ExplicitStatic extends Cleanser{
String s2 = s;
String baseS = super.baseS; //(c)调用的是最近的成员Cleanser
ExplicitStatic(){
super("ExplicitStatic");
System.out.println("ExplicitStatic():s2 = " + s2 + ", baseS = "
+ baseS + "super.baseS = " + super.baseS);
baseS = "ExplicitStatic";
System.out.println("baseS = " + baseS + " , super.baseS = " + super.baseS);
}
public static void main(String[] args){
ExplicitStatic x = new ExplicitStatic();
}
}
六、组合与继承之间选择
1)继承表示的是一种“is-a(是一个)”的关系,如货车是汽车中的一种;组合表示的是一种“has-a(有一个)”的关系,如汽车有四个轮子。
2)是否需要将新的class向上转型为base class。
七、在继承中的访问权限,protected
protect变量能被子孙类所调用。如Base class中的baseS能被Cleanser class和ExplicitStatic class调用。
class Villan{
private String nameString;
protected void set(String nm) {
nameString=nm;
}
public Villan(String namString) {
this.nameString=namString;
}
public String toString() {
return "i am is villan and my name is "+nameString;
}
}
public class Orc extends Villan{
private int num;
public Orc(String namString,int num) {
super(namString);
this.num=num;
// TODO Auto-generated constructor stub
}
public void change(String namString,int num) {
set(namString);
this.num=num;
}
public String toString() {
return ""+num+":"+super.toString();
}
public static void main(String[] args) {
// TODO Auto-generated method stub
Orc orc =new Orc("liburger", 12);
System.out.println(orc);
orc.change("bob", 9);
System.out.println(orc);
}
}
八、Final关键字
1、final data
1)当基本类型被定义为final时,它的数据值不能被改变。如下:
class Values{
final inti=1;
//i++;报错
//Syntax error on token "++", invalid VariableDeclarator
}
2)当object reference被定义为final时,不能改变的是reference而不是对象本身
class Values{
inti=1;
//i++;
//Syntax error on token "++", invalid VariableDeclarator
}
public class FinalTest {
public staticvoid main(String[] args) {
final Valuesvalues =new Values();
values.i++;
values.i=2;
Values values2 = new Values();
values =new Values();//报错
//不能通过new Value()使v重新指向一个对象
}
}
由于v为final,所以不能通过new Value()使v重新指向一个对象;但是v所指向的对象的值是可以改变的(v.i++)。
2、Blank final
我们可以声明数据类型为final但不赋值,这就是blank final。但是blank final必须在构造函数中赋值
class Values{
finalint i=1;
finalint j;
Values(){
j=1;//必须在构造函数中给blank final 赋值
// i++;//The final field Values.i cannot be assigned
}
//i++;
//Syntax error on token "++", invalid VariableDeclarator
}
public class FinalTest {
public staticvoid main(String[] args) {
final Valuesvalues = new Values();
// values.i++;//The final field Values.i cannot be assigned
Valuesvalues2 = new Values();
//values = new Values();
//不能通过new Value()使v重新指向一个对象
}
}
}
}
3、Final method
1)被声明为final的函数不能被覆写
2)class中所有private函数自然而然会是final
4、Final classes
1)当一个class被声明为final时,表示它不能被继承,但class的数据成员不是final,可以被改变。如
class Ai{
}
final class Bi{
int i=1;
int j=2;
Ai a =new Ai();
void f(){}
}
//class Ci extendsBi{}
//报错bi声明为final,表示不能被继承
public class FinalClass {
public staticvoid main(String[] args) {
Bi bi = new Bi();
bi.i=2;//final class中的non-final数据成员可以被改变
bi.j++;
}
}
2)final class中的所有函数也都自然是final,因为没有人能够加以覆写。
九、确保正确清理
C++中析构函数概念:析构函数是一种在对象被销毁时可以被自动调用的函数。
Java中,我们并不知道垃圾回收器何时会被调用,所以需要显示的编写一些特殊的方法来销毁对象。
使用语法:
Try{
}finally{
X.dispose()清理方法
}
十、名称屏蔽
Java的基类拥有某个已被多次重载的方法名称,那么在导出类中重新定义该方法名称并不会屏蔽其在基类中的任何版本。因此,无论是在该层或者它的基类中对方法进行定义。
package com.myfirst.test;
class Homer{
//基类中的方法重载
char doh(char c){
System.out.println("doh(char c)");
return 'd';
}
void doh(Milhouse m) {
}
//void doh(Milhouse m) {
//System.out.println("Homerdoh(Milhouse m)");
//}
float doh(float f){
System.out.println("doh(float f)");
return 'f';
}
}
class Milhouse{
}
class Bart extends Homer{
@Overridevoid doh(Milhouse m){
System.out.println("doh(Milhouse m)");
}
//错误提示The methoddoh(Milhouse) of typeBart must override or implement asupertype method
//@Override 注解可以防止你在不想重载时而意外的进行了重载
}
public class Hide {
public staticvoid main(String[] args) {
Bart bart = new Bart();
bart.doh(1);
bart.doh('x');
bart.doh(1.0f);
bart.doh(new Milhouse());
}
}