JAVA从入门到放弃(5)复用类

J

这篇笔记中我想简述一下JAVA的众多引人注目的功能之一:复用。真正的复用不仅要做到能够复制代码并对之加以改变,还应该做到更多。

复用的方法包括:组合、继承、代理。下面我们首先简述一下组合

所谓的组合就是在新的类中产生现有类的对象。因为新产生的类是由现有类和对象组成的,所以这种方法称为组合。下面的例子给出了组合的用法

从这个代码中可以看到组合的具体使用示例,在注释部分就是组合的使用,就是在新建类中使用现有类,对于输出结果可以对照初始化方面的相关知识,这里需要知道的是编译器并不是简单的为每一个引用都创建默认对象,我们如果想要初始化这些引用,可以在如下位置进行初始化操作

1.在定义对象的地方

2.在类的构造器中

3.证要使用这些对象之前

4.直接使用实例进行初始化

其中第一条比较常用,这也就是保证在调用前得到初始化。第三条对应的方式叫做惰性初始化,使用惰性初始化在对象不值得以及不必要的时候就不比对对象进行实例化,就可以减少负担,下面展示一下四种方法

下面说一下在面向对象中比较常见的一个复用方法,就是继承方法。当创建一个类时,这个类总是在继承,当指明继承对象时是继承知名对象,当没有指明继承对象是默认继承object类(标准根类)

当我们在使用继承时需要使用extends来进行集成,这表示新类与旧类相似,这里我们叫基类和导出类,首先看一下具体实现

大概就是这样,这样就实现了一个继承。当然我们可以为每个类创建一个main函数,当我们在命令行里输入java BasicClass就会调用BasicClass.main(),当然也可以调用ExtandClass.main()。在继承中,我们可以把一些不想被继承的数据成员设置为private。只有protected和public可以被导出类所使用。既然出现了子类和父类,那么就需要解决如何在子类中调用父类的成员或者方法的问题,这里我们可以使用超类super,超类表示调用父类成员或方法,这个super在后面会详细介绍。

下面的内容就是关于如何初始化,你可能会说,初始化之前不是单独已经讨论过了吗?需要注意的是这里的初始化和上面阐述到的初始化略有不用,因为这里的初始化不仅要初始化导出类,而且还要初始化基类。当然在初始化导出类的时候初始化基类这个动作也也会自动执行,顺序是先初始化基类再初始化导出类。所以说整个的构建过程是从基类向外扩散。

在导出类中有一个地方需要注意,基类的初始化是会被JAVA自动调用,然而如果声明了构造器,JAVA并不会自动调用基类的构造器,因此在导出类的构造器中需要手动调用基类的构造器

就像上面展示的这样,如果不引用基类的构造器,编译器会提示你无法找到Game类,因为构造器是需要在创建类的时候被调用的。没有构造器就无法新建类

前面提到了复用的两个办法,现在要说的是复用的第三个方法:代理。所谓的代理就是继承和组合的私生子。所谓私生子就是JAVA并没有提供直接的支持,而是大家经常使用所以这里提到一下。

当然比较常见的就是使用组合和继承的交替使用。当然有的时候需要在组合与继承之间进行选择,需要知道组合是显式的在导出类放置子对象,而继承则是隐式的在导出类中放置子对象。组合通常用于想在新类中使用现有类的功能而非使用他的接口,继承通常使用在需要对现有类开发一个该类的特殊版本上。在继承中的关系是is-a关系,在组合中的关系是has-a关系

既然我们重载了方法,那么就会遇到一个重载基类部分方法的问题,如果你得某些基类方法不想被覆盖可以使用名称屏蔽@ Override。但是要注意的是Override并不是JAVA关键字,你一样可以将Override当成一个关键字使用,这个关键字会防止你在不想要进行重载时而意外的被重载。

既然我们已经介绍完了继承,那么现在可以详细介绍一下protected关键字了。protected想要将某些事物尽可能对这个世界隐藏起来,但仍然允许他的导出类使用protected成员。也就是说protected对于外部类来说是private,对于导出类活在一个包内的类来说相当于一个精简版的public。代码如下

既然提到了继承,就存在向下继承和向上转型。向下继承已经说完了,现在就应该说一下向上转型了。继承最重要的是表现新类和基类之间的关系。首先看下面代码

仔细研读一下上面的代码,我们会发现一个很有意思的问题在Instrument类中tune方法中接受的参数是Instrument类,而在Wind类中调用了Instrument.tune传入的则是Wind类,不是Instrument类,这看起来很难理解。这里就发生了一个向上转型,这里将Wind向上转型为Instrument,因为Wind是Instrument的导出类。所以在向上转型这个过程中总体来说是安全的,在向上转型的过程中可能发生的只是导出类方法的丢失,并不会影响向上转型的结果。既然有向上转型,自然就有向下转型,注意这里的向下转型并不是向下继承,这个留到日后再讨论

最后再需要讨论一个复用类中比较重要的一个东西-final。所谓的final是指无法改变的东西。final可以用在永不改变的常量。对于基本类型final使数值恒定不变,对于对象引用final使引用恒定不变,这里就相当于C/C++里的const。想要达到C/C++中#define的效果需要同时使用static和final,也就是说同时使用final和static的变量需要完全大写,也就是常规意义上的常量,而且全局只能有一个。

JAVA允许生成空白的final,所谓空白final就是被指明为final但并没有为其指定初值。这里就会出现一个奇怪的情况,编译器应该尽力保证每个对象在使用前都必须进行初始化。所以我们必须在域的定义处或每个构造器中使用表达式对final值进行赋值,这样就可以满足编译器的要求。

当然final也可以用在函数参数中,这样就意味着无法更改参数的引用所指向的对象。当然也可以对类中的某一个方法使用final,这样可以把方法锁定,可以防止任何继承类修改这个方法,每一个private都被隐式的定义为final。以此类推final自然也可以应用在类上,就以为着这个类不可以被继承,由于final是对类应用,所以类内元素和方法也都隐式的被声明为final

About the author

NOBUG.IN

Add comment

By NOBUG.IN

Your sidebar area is currently empty. Hurry up and add some widgets.