Java的继承、抽象、组合
类的继承
类的继承
一种由已有的类创建新类的机制,是面向对象程序设计的基石之一。通过继承,可以根据已有类来定义新类,新类拥有已有类的所有功能。
Java只支持类的单继承,每个子类只能有一个直接父类
父类是所有子类的公共属性及方法的集合,子类则是父类的特殊化
继承机制可以提高程序的抽象程度,提高代码的可重用性
基类和派生类
-
基类(base class)
也称超类(superclass)是被直接或间接继承的类 -
派生类(derived-class)
也称子类 (subclass)继承其他类而得到的类
继承所有祖先的状态和行为
派生类可以增加变量和方法
派生类也可以覆盖(override)继承的方法
子类对象与父类对象存在“IS A”(或“is a kind of”)的关系
派生类产生的对象从外部来看,它应该包括
- 与基类相同的接口
- 可以具有更多的方法和数据成员
其内包含着一个基类类型的子对象
继承语法:
class childClass extends parentClass
{
//类体
}
举个例子,在一个公司中,有普通员工(Employees)及管理人员(Magagers)两类人员
职员对象(Employees)可能有的属性信息包括
员工号(employeeNumber)
姓名(name)
地址(address)
电话号码(phoneNumber)
管理人员(Managers)除具有普通员工的属性外,还可能具有下面的属性
职责(responsibilities)
所管理的职员(listOfEmployees)
//父类Employee
class Employee
{
int employeeNumbe ;
String name, address, phoneNumber ;
}
//子类Manager
class Manager extends Employee
{
//子类增加的数据成员
String responsibilities, listOfEmployees;
}
需要注意的:
子类不能直接访问从父类中继承的私有属性及方法,但可使用公有(及保护)方法进行访问
public class B {
public int a = 10;
private int b = 20;
protected int c = 30;
public int getB() { return b; }
}
public class A extends B {
public int d;
public void tryVariables() {
System.out.println(a); //允许
System.out.println(b); //不允许
System.out.println(getB()); //允许
System.out.println(c); //允许
}
}
隐藏和覆盖
子类对从父类继承来的属性变量及方法可以重新定义
属性的隐藏
class Parent {
Number aNumber;
}
class Child extends Parent {
Float aNumber;
}
- 子类中声明了与父类中相同的成员变量名,则从父类继承的变量将被隐藏
- 子类拥有了两个相同名字的变量,一个继承自父类,另一个由自己声明
- 当子类执行继承自父类的操作时,处理的是继承自父类的变量;而当子类执行它自己声明的方法时,所操作的就是它自己声明的变量
访问被隐藏的父类属性
- 调用从父类继承的方法,则操作的是从父类继承的属性
- 使用super.属性
class A1
{ int x = 2;
public void setx(int i)
{ x = i; }
void printa()
{System.out.println(x);}
}
class B1 extends A1
{ int x=100;
void printb()
{ super.x = super.x +10 ;
System.out.println("super.x=" + super.x + " x= " + x);
}
}
public class Exam4_4Test
{ public static void main(String[] args)
{ A1 a1 = new A1();
a1.setx(4);
a1.printa();
B1 b1 = new B1();
b1.printb();
b1.printa();
b1.setx(6); // 将继承x值设置为6
b1.printb();
b1.printa();
a1.printa();
}
}
运行结果:
4
super.x= 12 x= 100
12
super.x= 16 x= 100
16
4
操作父类静态属性举例
子类不能继承父类中的静态属性,但可以对父类中的静态属性进行操作。如在上面的例子中,将“int x = 2;”改为“static int x = 2;”,再编译及运行程序,会得到下面的结果
4 super.x= 14 x= 100 14 super.x= 16 x= 100 16 16
在上面的结果中,第一行及最后一行都是语句“a1.printa();”输出的结果,显然类B中的printb()方法修改的是类A中的静态属性x
方法覆盖
如果子类不需使用从父类继承来的方法的功能,则可以声明自己的同名方法,称为方法覆盖
覆盖方法的返回类型,方法名称,参数的个数及类型必须和被覆盖的方法一模一样
只需在方法名前面使用不同的类名或不同类的对象名即可区分覆盖方法和被覆盖方法
覆盖方法的访问权限可以比被覆盖的宽松,但是不能更为严格
方法覆盖的应用场合
- 子类中实现与父类相同的功能,但采用不同的算法或公式
- 在名字相同的方法中,要做比父类更多的事情
- 在子类中需要取消从父类继承的方法
注意事项
必须覆盖的方法
派生类必须覆盖基类中的抽象的方法,否则派生类自身也成为抽象类.
不能覆盖的方法
基类中声明为final的终结方法
基类中声明为static 的静态方法
调用被覆盖的方法
super.overriddenMethodName();
Object类
Java程序中所有类的直接或间接父类,类库中所有类的父类,处在类层次最高点
包含了所有Java类的公共属性,其构造方法是Object( )
包含的主要方法
Object类定义了所有对象必须具有的状态和行为,较主要的方法如下
-
public final Class getClass()
获取当前对象所属的类信息,返回Class对象 -
public String toString()
返回当前对象本身的有关信息,按字符串对象返回 -
public boolean equals(Object obj)
比较两个对象是否是同一对象,是则返回true -
protected Object clone( )
生成当前对象的一个拷贝,并返回这个复制对象 -
Public int hashCode()
返回该对象的哈希代码值 -
protected void finalize() throws Throwable
定义回收当前对象时所需完成的资源释放工作
你的类不可以覆盖终结方法,即有final修饰的方法
相等和同一
两个对象具有相同的类型,及相同的属性值,则称二者相等(equal)
如果两个引用变量指向的是同一个对象,则称这两个变量(对象)同一(identical)
两个对象同一,则肯定相等
两个对象相等,不一定同一
比较运算符“==” 判断的是这两个对象是否同一
clone方法
根据已存在的对象构造一个新的对象
在根类Object 中被定义为protected,所以需要覆盖为public
实现Cloneable 接口,赋予一个对象被克隆的能力(cloneability)
class MyObject implements Cloneable
{ //…
}
finalize方法
在对象被垃圾回收器回收之前,系统自动调用对象的finalize方法
如果要覆盖finalize方法,覆盖方法的最后必须调用super.finalize
getClass方法
final 方法,返回一个Class对象,用来代表对象隶属的类
通过Class 对象,你可以查询Class对象的各种信息:比如它的名字,它的基类,它所实现接口的名字等。
void PrintClassName(Object obj) {
System.out.println("The Object's class is " +
obj.getClass().getName());
}
notify、notifyAll、wait方法
final方法,不能覆盖
这三个方法主要用在多线程程序中
终结类与终结方法
- 被final修饰符修饰的类和方法
- 终结类不能被继承
- 终结方法不能被当前类的子类重写
终结类
- 终结类的特点
不能有派生类 - 终结类存在的理由
安全: 黑客用来搅乱系统的一个手法是建立一个类的派生类,然后用他们的类代替原来的类
设计: 你认为你的类是最好的或从概念上你的类不应该有任何派生类
举例:
声明ChessAlgorithm 类为final 类
final class ChessAlgorithm { . . . }
如果写下如下程序:
class BetterChessAlgorithm extends ChessAlgorithm { … }
编译器将显示一个错误
Chess.java:6: Can’t subclass final classes: class ChessAlgorithm
class BetterChessAlgorithm extends ChessAlgorithm {
^
1 error
终结方法
- 特点
不能被派生类覆盖 - 终结方法存在的理由
对于一些比较重要且不希望子类进行更改的方法,可以声明为终结方法。可防止子类对父类关键方法的错误重写,增加了代码的安全性和正确性
提高运行效率。通常,当java运行环境(如java解释器)运行方法时,它将首先在当前类中查找该方法,接下来在其超类中查找,并一直沿类层次向上查找,直到找到该方法为止
抽象类
代表一个抽象概念的类
没有具体实例对象的类,不能使用new方法进行实例化
类前需加修饰符abstract
可包含常规类能够包含的任何东西,例如构造方法,非抽象方法
也可包含抽象方法,这种方法只有方法的声明,而没有方法的实现
- 存在意义
抽象类是类层次中较高层次的概括,抽象类的作用是让其他类来继承它的抽象化的特征。
抽象类中可以包括被它的所有子类共享的公共行为
抽象类可以包括被它的所有子类共享的公共属性
在程序中不能用抽象类作为模板来创建对象;
在用户生成实例时强迫用户生成更具体的实例,保证代码的安全性
几何形状的例子
将所有图形的公共属性及方法抽象到抽象类Shape。再将2D及3D对象的特性分别抽取出来,形成两个抽象类TwoDimensionalShape及ThreeDimensionalShape
- 2D图形包括Circles、Triangles、Rectangles和Squares
- 3D图形包括Cube、Sphere、或Tetrahedron
- 在UML中,抽象类的类名为斜体,以与具体类相区别
抽象类声明的语法形式为
abstract class Number {
. . .
}
如果写:
new Number();
编译器将显示错误
抽象方法
声明的语法形式为
public abstract <returnType> <methodName>(...);
仅有方法头,而没有方法体和操作实现
具体实现由当前类的不同子类在它们各自的类声明中完成
抽象类可以包含抽象方法
需注意的问题
- 一个抽象类的子类如果不是抽象类,则它必须为父类中的所有抽象方法书写方法体,即重写父类中的所有抽象方法
- 只有抽象类才能具有抽象方法,即如果一个类中含有抽象方法,则必须将这个类声明为抽象类
- 除了抽象方法,抽象类中还可以包括非抽象方法
抽象方法的优点
隐藏具体的细节信息,所有的子类使用的都是相同的方法头,其中包含了调用该方法时需要了解的全部信息
强迫子类完成指定的行为,规定其子类需要用到的“标准”行为
一个绘图的例子
各种图形都需要实现绘图方法,可在它们的抽象父类中声明一个draw抽象方法
abstract class GraphicObject {
int x, y;
void moveTo(int newX, int newY) { . . . }
abstract void draw();
}
然后在每一个子类中重写draw方法,例如:
class Circle extends GraphicObject {
void draw() { . . . }
}
class Rectangle extends GraphicObject {
void draw() { . . . }
}
贷款(Loan)分为许多种类,如租借(Lease)、抵押(Mortgage)、房屋贷款(HouseLoan)、汽车贷款(CarLoan)等
将Loan声明为抽象类,并指定所有的子类对象都应具有的行为,如计算月还款值(calculateMonthlyPayment),还款(makePayment),取得客户信息(getClientInfo),其中前两个因贷款种类不同计算方法也不同,可声明为抽象方法,Loan的所有子类都必须对这两个抽象方法进行重写
public abstract class Loan {
public abstract float calculateMonthlyPayment();
public abstract void makePayment(float amount);
public Client getClientInfo() { }
}
包的应用
为了解决类名冲突,Java提供包来管理类名空间
Java利用包来组织相关的类,并控制访问权限
包是一种松散的类的集合,利用包来管理类,可实现类的共享与复用
同一包中的类在默认情况下可以互相访问,通常把需要在一起工作的类放在一个包里
Java基础类库
Java提供了用于语言开发的类库,称为Java基础类库(JFC,Java Foundational Class) ,也称应用程序编程接口(API,Application Programming Interface),分别放在不同的包中
Java提供的包主要有
java.lang,java.io,java.math,java.util
java.applet,java.awt,java.awt.datatransfer
java.awt.event,java.awt.image,java.beans
java.net,java.rmi,java.security,java.sql等