第 5章 进一步讨论对象和类本章内容
5.1 抽象数据类型1 5.2 对象的构造和初始化2
5.3 this引用3 5.4 子类4
5.5方法重写5 5.6 Java包6
5.7 类成员7 5.8 关键字 final 8
5.10 接口10
5.12 过时信息12
5.9 抽象类9
5.11 内部类11
抽象数据类型
抽象数据类型抽象数据类型是指基于一个逻辑类型的数据类型以及这个类型上的一组操作 (类 )。
每一个操作( 成员方法 )由它的输入、输出定义。抽象数据类型的定义并不涉及它的实现细节,这些实现细节对于抽象数据类型的用户是隐藏的
程序 5-1给出了 Date类型和 tomorrow操作间建立的一种联系抽象数据类型
Java在数据和操作间建立了较严格的联系,
即把方法和数据封装在一个类中。在程序中不是把方法描述为对数据的操作,而是把数据看作它知道如何修改自己,然后要求数据对它自己执行操作。
Date d=new Date(20,11,1998);
d.tomorrow();
把方法看做是数据的特性,而不把数据与方法分开,是 OOP的基本思想。
定义方法
定义抽象数据类型后,需要为这个类型的对象定义操作,也就是方法。格式如下:
<修饰符 ><返回类型 ><名字 >(<参数列表 >)<块 >
<名字 >是方法名,它必须使用合法的标识符。
<返回类型 >说明方法返回值的类型。
<修饰符 >段可以含几个不同的修饰符,其中限定访问权限的修饰符包括 public,protected和 private。
<参数列表 >是传送给方法的参数表。表中各元素间以逗号分隔,每个元素由一个类型和一个标识符组成。
<块 >表示方法体,是要实际执行的代码段。
增加方法
程序 5-2 Date类中增加 daysInMonth()和 printDate()
方法按值传送
,按值,传送自变量,即方法调用不会改变自变量的值
当对象实例作为自变量传送给方法时,自变量的值是对对象的引用,也就是说,传送给方法的是引用值。在方法内,这个引用值是不会被改变的,但可以修改该引用指向的对象内容。因此,当从方法中退出时,所修改的对象内容可以保留下来传值实例
程序 5-3 创建 pt对象,方法内局部变量 val赋初值 11。
调用方法 changeInt()后,val的值没有改变。字符串变量 str作为 changeStr的参数传入方法内,当从方法中退出后,其内容也没有变化。当对象 pt作为参数传给 changeObjValue()后,该引用所保存的地址不改变,而该地址内保存的内容可以变化,
因此退出方法后,pt对象中的 ptValue改变为 99f
重载方法名
如果需要在同一个类中写多个方法,让它们对不同的变量进行同样的操作,就需要重载方法名
一个方法区别于另一个方法的要素:方法名、
参数列表及返回值。根据参数来查找适当的方法并调用,包括参数的个数及类型
方法的重载允许 Java 在同一个类中可以定义相同的方法名,但需要具有不同的参数表。不只如此,在不同的类中也可以定义相同的方法名现在假定需要打印 int,float和
String类型的值。
public void print(int i)
public void print(float f)
public void print(String s)
重载方法两条规则
调用语句的自变量列表必须足够判明要调用的是哪个方法
自变量的类型可能要进行正常的扩展提升 ( 如浮点变为双精度 ),但在有些情况下这会引起混淆
方法的返回类型可能不同
如果两个同名方法只有返回类型不同,而自变量列表完全相同则是不够的,因为在方法执行前不知道能得到什么类型的返回值,因此也就不能确定要调用的是哪个方法 。 重载方法的参数表必须不同,即参数个数或参数类型不同对象的构造和初始化
说明了一个引用后,要调用 new为新对象分配空间,也就是要调用构造函数
在 Java中,使用构造函数( constructor,也称为构造方法)是生成实例对象的唯一方法。在调用 new时,既可以带有变量,也可以不带变量,这要视具体的构造方法而定
调用构造方法时步骤如下:
(1) 分配新对象的空间,并进行缺省的初始化 。 在 Java
中,这两步是不可分的,从而可确保不会有没有初值的对象
(2) 执行显式的成员初始化
(3) 执行构造方法,构造方法是一个特殊的方法显式成员初始化
在成员说明中写有简单的赋值表达式,就可以在构造对象时进行显式的成员初始化
public class Initialized {
private int x = 5;
private String name = "Fred";
private Date created = new Date();
...
}
构造方法
构造方法是特殊的类方法,有着特殊的功能。它的名字与类名相同,没有返回值,
在创建对象实例时由 new运算符自动调用
为了创建实例的方便,一个类可以有多个具有不同参数列表的构造方法,即构造方法可以重载
构造方法不能说明为 native,abstract,
synchronized或 final,也不能从父类继承构造方法
public class Xyz {
// 成员变量
int x;
public Xyz() {//参数表为空的构造方法
// 创建对象
x = 0;
}
public Xyz(int i) {//带一个参数的构造方法
// 使用参数创建对象
x = i;
}
}
在创建 Xyz的实例时,可以使用两种形式:
Xyz Xyz1 = new Xyz();
Xyz Xyz2 = new Xyz(5);
构造方法的特性
构造方法的名字与类名相同
没有返回值类型
必须为所有的变量赋初值
通常要说明为 public类型的,即公有的
可以按需包含所需的参数列表缺省的构造方法
每个类都必须至少有一个构造方法。如果程序员没有为类定义构造方法,系统会自动为该类生成一个缺省的构造方法
缺省构造方法的参数列表及方法体均为空,所生成的对象的属性值也为零或空。如果程序员定义了一个或多个构造方法,则将自动屏蔽掉缺省的构造方法。构造方法不能继承缺省的构造方法 例
class BankAccount{
String ownerName;
int accountNumber;
float balance;
}
public class BankTester{
public static void main(String args[]){
BankAccount myAccount = new BankAccount();
System.out.println("ownerName=" + myAccount.ownerName);
System.out.println("accountNumber=" +
myAccount.accountNumber);
System.out.println("balance=" + myAccount.balance);
}
}
输出结果为:
ownerName=null
accountNumber=0
balance=0.0
我们可以在调用缺省的构造函数之后直接对其状态进行初始化,
BankAccount myAccount;
myAccount = new BankAccount();
myAccount.ownerName =,Wangli" ;
myAccount.accountNumber = 1000234;
myAccount.balance = 2000.00f;
构造方法重载
在进行对象实例化时可能会遇到许多不同情况,于是要求针对所给定的不同的参数调用各个不同的构造方法
在一个类中同时定义若干个构造方法,亦即对构造方法进行重载来实现
在其中一个构造方法中引用另一个构造方法的情况。可以使用关键字 this来指代本类中的其他构造方法
public class Student{
String name;
int age;
public Student(String s,int n){
name = s;
age = n;
}
public Student(String s){
this(s,20);
}
public Student(){
this("Unknown");
}
}
finalize方法
finalize方法 属于 Object类,它可被所有类使用。如果对象实例不被任何变量引用时,
Java会自动进行,垃圾回收,,收回该实例所占用的内存空间
在对对象实例进行垃圾收集之前,Java自动调用对象的 finalize方法,它相当于 C++ 中的析构方法,用来释放对象所占用的系统资源
finalize方法的说明方式如下:
protected void finalize () throws Throwable
this引用
类的成员方法中访问类的成员变量,可以使用关键字 this指明要操作的对象
在类方法中,Java自动用 this关键字 把所有变量和方法引用结合在一起。 this的使用是不必要的,
程序中可以不写该关键字
有些情况下关键字 this是必需的
例如,在完全独立的类中调用一个方法,同时把对象实例作为一个自变量来传送。此时,要用 this指明对哪个对象实例进行操作
Birthday bDay = new Birthday (this);
public class Date {
private int day,month,year;
public void printDate() {
System.out.println("The current date is (dd / mm / yy),"
+ this.day + " / " + this.month + " / " + this.year);
}
}
子类
Java中的类层次结构为树状结构,这和我们在自然界中描述一个事物是类似的。例如我们可以将动物划分为哺乳类动物及爬行类动物,然后又对这两类动物继续细分
,is a,关系
一般与特殊的关系
雇员 Employee,从这个最初的模型派生出多个具体化的版本,如经理 Manager。 显然,一名
Manager首先是一位 Employee,他具有
Employee的一般特性。除此之外,Manager还有 Employee所不具有的额外特性
public class Employee {
private String name;
private Date hireDate;
private Date dateOfBirth;
private String jobTitle;
private int grade;
...
public class Manager {
private String name;
private Date hireDate;
private Date dateOfBirth;
private String jobTitle;
private int grade;
private String department;
private Employee [] subordinates;
...
}
单重继承
如果一个类有父类,则其父类只能有一个,
Java只允许从一个类中扩展类。这条限制叫单重继承
让代码的可靠性更高。
为了保留多重继承的功能,提出了接口的概念
一个对象从其所有的父类(在树中通往
Object的路径上的类)中继承属性及行为。
不能继承构造方法
Object类
Object类是 Java程序中所有类的直接或间接父类,
也是类库中所有类的父类,处在类层次最高点。
所有其他的类都是从 Object类派生出来的,Object
类包含了所有 Java类的公共属性,其构造方法是
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//定义回收当前对象时所需完成的资源释放工作。
例 5-11
例 5-11 Employee及 Manager 类的对象可以使用其父类中定义的公有(及保护)属性和方法,就如同在其自己的类中定义一样
例 5-12 子类不能直接存取其父类中的私有属性及方法,但可以使用公有(及保护)
方法进行存取多态性
对象是 多态 的,即它们有,许多形式,
所有类的父类,就是 java.lang.Object类
Object类方法 toString()
对象内容转换为字符串 。
例 5-10的定义是下面定义的简写方式:
public class Employee extends Object
public class Manager extends Employee
方法自变量和异类集合
方法的参量
为了处理的一般性,实例和变量并不总是属于同一类
public TaxRate findTaxRate(Employee e) {
// 进行计算并返回 e的税率
}
// 而在应用程序类中可以写下面的语句
Manager m = new Manager();
……
TaxRate t = findTaxRate(m);TaxRate t = findTaxRate(m);
findTaxRate()的自变量必须是 Employee 类型的,而调用时的参数是
Manager类型的,但因为 Manager 是一个
Employee,所以可以使用更,一般,的变量类型来调用这个方法 。
异类集合
异类集合是由不同质内容组成的集合,也就是集合内所含元素的类型不完全一致
在面向对象的语言中,可以创建有公共祖先类的任何元素的集合
可以使用 Object的一个数组作为任何对象的容器异类集合
Employee [] staff = new Employee[1024];
staff[0] = new Manager();
staff[1] = new Employee();
数组 staff中各元素的类型是不相同的。可以像处理同质数组一样,处理由不同类型元素组成的数组。比如,可以对数组元素按年龄大小进行排序等
instanceof运算符
由于类的多态性,类的变量既可以指向本类实例,又可以指向其子类的实例。可以通过 instanceof运算符判明一个引用到底指向哪个实例
instanceof运算符类的继承关系如下所示:
public class Employee extends Object
public class Manager extends Employee
public class Contractor extends Employee
Object
Employee
Manager Contractor
instanceof运算符
public void method(Employee e) {
if (e instanceof Manager) {
// 经理级人士
}
else if (e instanceof Contractor) {
// 掌握公司机密的高层人士
}
else {
// 普通雇员
}
}
转换对象
使用对象之父类类型的一个变量指示该对象,称为 转换对象 ( casting)
例如,Employee e = new Manager();
Manager m = new Employee();
不能把父类的实例赋给子类的引用转换对象
如果用 instanceof运算符已判明父类的引用指向的是子类实例,就可以转换该引用,
恢复对象的全部功能
public void method(Employee e) {
if (e instanceof Manager) {
Manager m = (Manager)e;
System.out.println("This is the manager of " + m.department);
}
// 其他操作
}
转换对象
要替换对象引用时须做下列检查
沿类层次向,上,转换总是合法的
对于向,下,替换,只能是父类到子类转换,
其他类之间是不允许的
编译器检查正确后,需在运算时检查引用类型方法重写
使用类的继承关系,可以从已有的类产生一个新类,在原有特性基础上,增加了新的特性,父类中原有的方法不能满足新的要求,因此需要修改父类中已有的方法 。 又或者如果子类不需要使用从父类继承来的方法的功能,则可以定义自己的方法 。 这就是 重写 ( Override) 的概念,也称为方法的隐藏 。 子类中定义方法所用的名字,返回类型及参数表和父类中方法使用的完全一样,称子类方法重写了父类中的方法,从逻辑上看就是子类中的成员方法将隐藏父类中的同名方法 。
方法重写
子类重写父类方法多发生在这三种情况下
子类要做与父类不同的事情;在子类中取消这个方法;子类要做比父类更多的事情。
重写的同名方法中,子类方法不能比父类方法的访问权限更严格
如果父类中方法 method()的访问权限是 public,
子类中就不能含有 private的 method(),否则,会出现编译错误 。
程序 5-4
程序 5-4
例 5-16
public class Employee {
String name;
int salary;
public String getDetails() {
return "Name," + name + "\n" + "Salary," + salary;
}
}
public class Manager extends Employee{
String department;
public String getDetails() {
return "Name," + name + "\n" + "Manager of " + department;
}
}
程序 5-5
程序 5-5是在前面定义的二、三维点类基础上,增加求该点到原点距离的方法
super关键字
如果子类已经重写了父类中的方法,但在子类中还想使用父类中被隐藏的方法,可以使用 super关键字
在程序 5-5的子类 Point3d的 print()方法中增加一条语句,见程序 5-6
super关键字
如果方法名相同,而参数表不同,则是对方法的重载。调用重载方法时,编译器将根据参数的个数和类型,选择对应的方法执行
程序 5-7进一步说明 super的使用方法应用重写的规则
重写方法的允许访问范围不能小于原方法
重写方法所抛出的异常不能比原方法更多例 5-17
class SuperClass{
public void method(){
……
}
}
class SubClass extends SuperClass{
private void method(){
……
}
}
public class Test{
public static void main(String args[]){
SuperClass s1 = new SuperClass();
SuperClass s2 = new SubClass();
s1.method();
s2.method();
}
}
编译后会出现以下错误信息:
Test.Java:6,Methods can't be
overridden to be more private,
Method void method() is public in
class SuperClass.
private void method(){
例 5-18
import java.io.*;
class Parent{
void method(){
}
}
class Child extends Parent{
void method() throws IOException{
}
}
编译后会出现以下错误信息:
Child.Java:8,Invalid exception class
Java.io.IOException in throws clause,The
exception must be a subclass of an exception
thrown by void method() from class Parent.
void method() throws IOException{
父类构造方法调用
一个父类的对象要在子类运行前完全初始化 。
super关键字也可以用于构造方法中,其功能为调用父类的构造方法。子类不能从父类继承构造方法在子类的构造方法中调用某一个父类构造方法
如果在子类的构造方法的定义中没有明确调用父类的构造方法,则系统自动调用父类的缺省构造方法(即无参数的构造方法)
调用了父类的构造方法语句必须出现在子类构造方法的第一行例 5-19
class Employee{
String name;
public Employee(String s){
name = s;
}
}
class Manager extends Employee{
String department;
public Manager(String s,String d){
super(s);
department = d;
}
}
department = d;
super(s);
Java包
一个 Java源代码文件称为一个编译单元。一个编译单元中只能有一个 public类,且该类名与文件名相同。每个类都产生一个,class文件。这种机制下,
不同名的类中如果含有相同名字的方法或成员变量,不会造成混淆,但由于有继承机制,有可能程序员用到了其他人定义的类,比如是从互联网上下载的类,使用过程中又不知晓全部的类名,这就有冲突的可能了。所以在 Java中必须要有一种对名字空间的完全控制机制,以便可以建立一个唯一的类名。这就是 包机制,用于类名空间的管理
Java包的概念
包是类的容器,包的设计人员利用包来划分名字空间,用于分隔类名空间,以避免类名冲突,没有包定义的源代码文件成为未命名的包中的一部分,在未命名的包中的类不需要写包标识符
使用 package指明源文件中的类属于哪个具体的包。包语句的格式为:
package pkg1[.pkg2[.pkg3...]];
Java包的概念
package语句 一定是源文件中的第一条可执行语句,它的前面只能有注释或空行。一个文件中最多只能有一条 package语句
包的名字有 层次关系,各层之间以点分隔。
包层次必须与 Java开发系统的文件系统结构相同。通常包名中全部用小写字母
一个包可以包含若干个类文件,还可包含若干个包。一个包要放在指定目录下,通常用 classpath 指定搜寻包的路径。包名本身对应一个目录(用一个目录表示 )
Java包的概念
package java.awt.image;
在 Windows系统下,此文件必须存放在
java\awt\image目录下;如果在 unix系统下,
文件须放在 java/awt/image目录下。此定义语句说明当前的编译单元是包
java.awt.image的一部分,文件中的每一个类名前都有前缀 java.awt.image,因此不再会有重名问题
import语句
使用 MyClass类,或 mypackage包中的其它
public类,则需要使用使用全名
mypackage.MyClass m = new mypackage.MyClass();
先使用 import语句 引入所需要的类,程序中无需再使用全名
import mypackage.*;
//…
MyClass m = new MyClass(); package mypackage;public class MyClass {
//…
}
import语句
import语句 只用来将其他包中的类引入当前名字空间中,程序中不需再引用同一个包或该包的任何元素,而当前包总是处于当前名字空间中
引入语句的格式如下
import pkg1[.pkg2[.pkg3...]].(类名 |*)
,*”表示引入所有类例 5-20
假设有一个包 a,在 a中的一个文件内定义了两个类 xx和 yy,其格式如下:
package a;
class xx{
...
}
class yy{
...
}
当在另外一个包 b中的文件 zz.java中使用 a中的类时,语句形式如下:
// zz.java
package b; //说明是在包 b中
import a.*; //引入 a中的全部类
class zz extends xx {
yy y; //使用的是 a中的 yy类
...
}
CLASSPATH
环境变量 classpath将指示着 javac编译器如何查找所需要的对象
在编译命令中使用 -d选项,则 Java编译器可以创建包目录,并把生成的类文件放到该目录中
c:\>javac -d destpath Test.java
则编译器会自动在 destpath目录下建立一个子目录 p1,
并将生成的,class文件都放到 destpath/p1下访问权限与数据隐藏
day,month和 year是 private的,这意味着只能在 Date类中的方法内访问这些成员,而在类外的方法中不能访问它们,这就是访问权限( Access Control) 程序 5-1
一般使用方法来访问私有数据成员,这可保持数据的一致性和合法性。(参见例 5.24)
封装
强制使用者通过方法来访问数据是确保方法调用后各数据仍合法的更简单的方法
Date类的这个例子中,要计算当前日期的后继日,就必须通过 tomorrow方法得到。
封装的两个基本含义:属性和操作组合在类中;
尽可能隐藏对象的内部细节,只保留有限的对外接口,对数据的操作都通过这些接口来实现。
类成员
类成员,它包括 类变量 和 类方法 。它是不依赖于特定对象的内容
不同对象的成员其内存地址是不同的
系统只在实例化类的第一个对象的时候,
为类成员分配内存,以后再生成该类的实例对象时,将不再为类成员分配内存,不同对象的类变量将共享同一内存空间类成员成员变量成员方法成员变量成员方法类变量类方法对象 A 对象 B
类变量
让一个变量被类的多个实例对象所共享,
以实现多个对象之间的通信,或用于记录已被创建的对象的个数,这样的变量有时也被称为类变量(或静态变量)
将一个变量定义为类变量的方法就是将这个变量标记上关键字 static
程序 5-9
程序 5-8类变量(或静态变量)的作用在这个例子中,每一个被创建的对象得到一个唯一的 serial number,这个号码由初始值 1开始递增。由于变量 counter被定义为类变量,为所有对象所共享,因而当一个对象的构造方法将其递增 1
后,下一个将要被创建的对象所看到的 counter值就是递增之后的值类变量
Java语言中没有全局变量的概念,类变量从某种意义上来说相当于其他程序设计语言中的全局变量
类变量是唯一为类中所有对象共享的变量
如果一个类变量同时还被定义为 public类型,那么其他类也同样可以使用这一变量,
引用这一变量时甚至无须生成一个该类的对象,而是直接利用类名即可指向它程序 5-9
程序 5-9
类方法
标记上关键字 static的方法称为类方法(或称静态方法)
程序 5-10
例 5-25
public class Converter {
public static int centigradeToFahrenheit(int cent){
return (cent * 9 / 5 + 32);
}
}
调用类方法时,前缀使用是的类名,而不是对象实例名,如下所示:
Converter.centigradeToFahrenheit(28);
如果从当前类中的其它方法中调用,则不需要写类名,可以直接写方法名:
centigradeToFahrenheit(28)
静态方法
由于静态方法可以在没有定义它所从属的类的对象的情况下加以调用,故不存在 this

静态方法不能被重写。也就是说,在这个类的子孙类中,不能有相同名称、相同参数的方法关键字 final
它既可以用来修饰一个类,也可用于修饰类中的成员变量或成员方法。
用这个关键字进行修饰的类或类的成员都是不能改变的。
如果一个方法被定义为 final,则不能被重写;
如果一个类被定义为 final,它不能有子类。
终极类
被标记为 final的类将不能被继承,这样的类可以称之为终极类( final class) 其声明的格式为:
final class finalClassName{
........
}
例 5-28
final public class FinalClass{
int memberar;
void memberMethod(){};
}
class SubFinalClass extends FinalClass{
int submembervar;
void subMemberMethod(){};
}
在编译时会出现以下错误信息:
SubFinalClass.Java:7,Can't subclass final
classes,class FinalClass
class SubFinalClass extends FinalClass{
终极方法
成员方法被标记为 final成为 终极方法( final
method),被标记 final的方法将不能被重写
安全考虑。这样调用 final类型的方法时可以确保被调用的是正确的、原始的方法
标记为 final有时也被用于优化
终极方法的定义格式为:
final returnType finalMethod([paramlist]){
........
}
例 5-29
class FinalMethodClass{
final void finalMethod (){
… //原程序代码
}
}
class OverloadClass extends FinalMethodClass{
void finalMethod(){ // 错误 !
… //子程序代码
}
}
终极变量
一个变量被标记为 final,则会使它成为一个常量
class Const{
final float PI = 3.14f;
final String language = "Java";
}
public class UseConst{
public static void main(String args[]){
Const myconst = new Const();
myconst.PI=3.1415926f;
}
}
在编译时会出现以下错误信息:
UseConst.Java:9,Can't
assign a value to a
final variable,PI
myconst.PI=3.1415926f;
终极变量
将程序中可能用到的一系列常量定义在一个类中,其他类通过引入该类来直接使用这些常量,保证常量使用的统一,修改提供方便
将一个引用类型的变量标记为 final,那么这个变量将不能再指向其他对象,但它所指对象的取值仍然是可以改变的例 5-31
class Car{
int number=1234;
}
class FinalVariable{
public static void main(String args[]){
final Car mycar = new Car();
mycar.number = 8888; //可以 !
mycar = new Car(); //错误 !
}
}
抽象类
定义了方法但没有定义具体实现的类通常称为 抽象类( abstract class)
通过关键字 abstract把一个类定义为抽象类
每一个未被定义具体实现的方法也应标记为
abstract( 可以称为抽象方法)
只有抽象类才能具有 抽象方法
不能用抽象类作为模板来创建对象,必须生成抽象类的一个非抽象的子类后才能创建实例
如果一个抽象类除了抽象方法外什么都没有,则使用接口更合适抽象类
抽象类的定义如下:
抽象方法的定义:
public abstract class Shape {
// 定义体
} //为使此类有用,它必须有子类
public abstract <returnType> <methodName>(参数列表 );
抽象类
程序 5-11详尽地说明了抽象类的作用
Stack:先进后出 FILO
(堆栈)
put,get
point
top bottom
put get
Queue:先进先出 FIFO
(队列)
例 5-32
public abstract class Drawing{
public abstract void drawDot(int x,int y);
public void drawLine(int x1,int y1,int x2,
int y2){
……// 重复使用 drawDot()方法,通过连续画点的方式画出线条
}
}
接口
接口( interface) 是抽象类功能的另一种实现方法,可将其想象为一个,纯,的抽象类
允许创建一个类的基本形式,包括方法名、
自变量列表以及返回类型,但不规定方法主体
接口中所有的方法都是抽象方法,都没有方法体
Java通过允许一个类实现( implements) 多个接口从而实现了比多重继承更加强大的能力,并具有更加清晰的结构接口的定义接口的定义形式为:
[接口修饰符 ] interface 接口名称 [extends 父类名 ]{
…… //方法原型或静态常量
}
接口本身也具有数据成员与方法,但数据成员一定要赋初值,且此值将不能再更改例 5-33
在接口中定义的成员变量都缺省为终极类变量,即系统会将其自动增加 final和 static这两个关键字,并且对该变量必须设置初值
interface CharStorage{
void put(char c);
char get();
}
例 5-34
public interface Insurable{
public int getNumber();
public int getCoverageAmount();
public double calculatePremium();
public Date getExpiryDate();
}
接口的实现
实现接口的类不从该接口的定义中继承任何行为,在实现该接口的类的任何对象中都能够调用这个接口中定义的方法。在实现的过程中,这个类还可以同时实现其他接口
要实现接口,可在一个类的声明中用关键字 implements表示该类已经实现的接口。完成接口的类必须实现接口中的所有抽象方法接口的实现
Implements语句的格式如下:
public class className implements
interfaceName {
/* Bodies for the interface methods */
/* Own data and methods,*/
}
例 5-34中接口实现
public class Car implements Insurable {
public int getPolicyNumber() {
// write code here
}
public double calculatePremium() {
// write code here
}
public Date getExpiryDate() {
// write code here
}
public int getCoverageAmount() {
// write code here
}
}
例 5-35
class Stack implements CharStorage{
private char mem[] = new char[10];
private int point = 0;
void put(char c){
mem[point] = c;
point++;
}
char get(){
point--;
return mem[point];
}
}
接口的实现
使用接口名称作为一个引用变量的类型也是允许的,该引用可以用来指向任何实现了该接口的类的实例程序 5-12
程序 5-12 接口应用实例。定义一个接口
Shape2D,可利用它来实现二维的几何形状类 Circle和 Rectangle
内部类
类名只能在定义的范围内被使用,内部类的名称必须区别于外部类
内部类可以使用外部类的类变量和实例变量,也可使用外部的局部变量
内部类可以定义为 abstract类型
内部类也可以是一个接口,这个接口必须由另一个内部类来实现内部类
内部类可以被定义为 private或 protected类型 。 当一个类中嵌套另一个类时,访问保护并不妨碍内部类使用外部类的成员
被定义为 static型的内部类将自动转化为顶层类,
它们不能再使用局部范围中或其他内部类中的数据和变量
内部类不能定义 static型成员,而只有顶层类才能定义 static型成员 。 如果内部类需要使用 static型成员,这个成员必须在外部类中加以定义
应用内部类的好处在于它是创建事件接收者的一个方便的方法程序 5-13
程序 5-13,TwoListenInner类使用两个内部类来处理鼠标事件匿名类
在定义一个内部类时,也可以将整个类的描述包含在一个表达式范围里,使用这种方法是在定义了一个匿名类的同时创建了一个对象
匿名类使代码变得更小但是代码的可读性也随之下降
使用匿名类重载 TwoListenInner的部分代码,
如 程序 5-14所示内部类的工作方式
内部类可以访问其外部类,因此 JDK1.1编译器给内部类另外添加了一个 private成员变量和一个构造方法对它进行初始化
编译器还自动使外部类的范围适用于内部类,将,,” 替换为,$” 。这样编译器看到的改动后的源代码为 程序 5-15
过时信息
JDK也在不断地发展和完善。从 JDK1.0到
JDK1.1之间的变化最为明显,选取 Java语言学习材料时一定要选用 JDK1.1以上版本
JDK1.1中采取了若干重要的改进,两个比较重大的变化,一个是 内部类( Inner class)
的概念,而另一个就是 对于方法名称的标准化