第五章 类、接口、包
关于对象与类,类变量、类方法、实例变量、实例方法,我们已在第 4章中作了概念性的介绍。现在我们要用程序实例进一步探讨对象向导程序设计的精神,
并介绍其他重要概念 。
我们要先从 Java的基本组件 ——类开始,
较深入地剖析它的构成,然后详细讲解接口与包 。
类组成组件类是 Java程序最小的基本单位。 Java编译器无法处理比类更小的程序代码。当我们说要着手开始写 Java程序,也就是要开始建立一个类( class)。 这些类有可能是顶层的抽象( abstract) 类,也有可能是直接继承某一个类的类。若是要写一个较大型的方案
( project) 时,便需要先规划好类上下层级关系,以及各类的存取控制特性 。
所以,我们在这里要来看一个类的组成及其运作规则。组成一个类的组件如下,
[import包 ]
[类修饰符 ] class xxxclass [extends超类 ]〔 implements 接口 〕

//类字段或称 类变量
//实例字段或称 实例变量
//静态初始者
//类方法
//实例方法
}
创建类的实例对象 ——new
new操作符返回一个引用,而不是指针,这意味着可以把结果赋给一个对象变量,而不是一个地址表达式。
Myclass obj=new Myclass();
在 java中,经常需要使用 new操作符来为对象分配实际内存空间,当定义一个对象变量时,仅仅是引用了一个对象,该变量在能够被使用前必须与内存中的一个实际对象(利用 new操作符分配的)相关联。
与 C++不同,java没有与 new操作符匹配的 delete操作符,也不必在源代码中显示地释放内存空间,java解释器利用垃圾收集进程来处理对象的撤消和内存管理。
new操作符可以用于创建一个或多个对象
Myclass obj[]=new Myclass [10];
注意:当定义基本数据类型变量 (int,short,long,float,double,boolean和 char)
或者字符串变量时,不必用 new操作符,基本数据类型数据在数据存储区中分配了真实的内存空间,String类型的对象可以用加引号字符串初始化 int a,b,c=200;
String name=“I love china”;
构造函数构造函数是一种特殊的方法。用于对象由类产生时,对对象内容做一些初值设置(初始化)。当 new一个类要产生实例时,便会自动调用该类的构造函数 。
构造函数的声明格式,
<修饰符>类名(<参数行>)〔 throws< 异常种类> 〕

< 构造函数主体>

以前面的轿车类为例:
public class Sedan extends Car{//构造函数

public Sedan() {
tiretype="BridgeStone185ST"; //轮胎型号
engine=1598.5f;//排气量

}
public static void main(String args[]){
Sedan sedan=new Sedan();




构造函数的调用是在用 new运算符创建类对象时由系统自动完成的,构造函数的参数传递和形实结合过程也是由系统调用的同时完成的作用:
1、构造函数保证每一个新建对象都处于正常合理的状态
2、使得初始化工作不仅仅包括成员变量的赋值,还可以有更复杂
Department(int dno,String dname,int total)
{
if(dno<=0) System.exit(0);
m_DeptNo=dno;
m_DeptName=new String(dname);
m_DeptTotalEmp=total;
}
类修饰符类修饰符除了 (package)以外有 3个,
Public——提供给其他类完全的存取权限。也就是说在同一包中的类可自由取用此类,而别的包中的类可通过 import关键词来引入此类所属的包加以运用 。
而用此修饰符的类有几个特性,
1,一个程序里只能有一个类被修饰为 public,否则编译会错。
Public xxxClass(){
}
class aaaClass(){
}
class bbbClass(){
}
2,源文件存储文件名,必须是用 public修饰的类名 ( xxxClass)
3,若程序中没有任何 public类,则文件名可任取。而如果文件名是程序中的一个类名,则该类被视作 public,可供别的类存取 。
类修饰符
Abstract——此字的英文意思是”抽象的”,”非实际的”。此修饰符可修饰类及方法。字段倒没有用到这个修饰符。用此修饰类是表示此类的功用不完全在于提供实际的程序代码,而只是先定义一些方法规格。然后让继承此类的次类来覆盖此规格的内容 。
abstract类与方法有下列特性,
1,一个抽象类里可以没有定义抽象方法。但只要类中有一个方法是被声明为 abstract,则该类必须为 abstract。
2,抽象类不能被实例化,即不能被 new生成一个实例对象。
3,若一个子类继承一个抽象类,则子类需用覆盖的方式来实化该抽象超类中的抽象方法。若没有完全实例化所有的抽象方法,则子类仍是抽象的。
抽象方法可再与 public,protected复合使用,但不能与 final,private和
static复合使用。
定义一个汽车抽象类范例 9_1:汽车抽象类
import java.awt.Color;
public abstract class Car {
//公用数据字段声明区
public Color color; //车辆颜色
public int gearNum; //排档数
public String tiretype; //轮胎型号
public float engine; //引擎排气量
//公用抽象方法声明区
public abstract void shiftgear(); //换档
public abstract void brake(); //煞车
public abstract void aircon(); //开冷气
public abstract void headlight(); //开大灯
}
范例 9_2:轿车类
import java.awt.Color;
public class Sedan extends Car{
static int gearNum=5;//声明 gearNum为类栏
public Sedan() {//
tiretype="BridgeStone185ST"; //输胎型号
engine=1598.5f;//排气量
}
public static void main(String args[]){
Sedan sedan=new Sedan(); //产生实例
sedan.equipment();
sedan.shiftgear ();
sedan.brake();
}
public void equipment(){//轿车的配备
System.out.println("轿车颜色,"+color);
System.out.println("轿车排档数,"+gearNum);
System.out.println("轿车轮胎型号,"+tiretype);
System.out.println("轿车排气量,"+engine);
}
public void shiftgear(){System.out.println("轿车换 档 方式:自排 "+ gearNum+"文件 ");}//换 档
public void brake(){System.out.println("水压式煞车系统 ");} //煞车
public void aircon(){}; //开冷气
public void headlight(){};//开大灯
}
执行结果,
轿车颜色,null
轿车排文件数,5
轿车轮胎型号,
BridgeStone185ST
轿车排气量,1598.5
轿车换文件方式:自排 5文件水压式煞车系统范例 9_3:我的车类
import java.awt.Color;
public class MyCar extends Sedan{
private Color color;
public MyCar() {
color=Color.blue; //设置车辆颜 色
}
public static void main(String args[]) {
MyCar mycar=new MyCar();
mycar.equipment();
mycar.shiftgear ();
mycar.brake();
}
public void equipment(){
System.out.println("我的爱车排 档 数,"+this.gearNum);
System.out.println("我的爱车颜色,"+this.color);
System.out.println("我的爱车轮胎型号,"+this.tiretype);
System.out.println("我的爱车引擎排气量,"+this.engine);
}
public void shiftgear(){//换 档 -覆盖的新方法
super.shiftgear();
System.out.println("我的爱车换 档 方式:自排 "+this.gearNum+"档 " );}
}
执行结果,
我的爱车排文件数,5
我的爱车颜色,
java.awt.Color[r=0,g=0,b=255]
我的爱车轮胎型号,BridgeStone185ST
我的爱车引擎排气量,1598.5
轿车换文件方式:自排 5文件我的爱车换文件方式:自排 5文件水压式煞车系统类修饰符
Final——表示此类为“最终”类,别的类不能继承此类,
其方法也不能被覆盖。例如 java.lang.System类即为
final类,
public final class System
extends Object
我们可使用该类,但不能继承并覆盖其内容。用重复的两个修饰符 public+final的意思是:此 final类可被
import来引用,但不能被继承。 System类关系到系统层级控制,为了安全性,故必须为 final类,以避免被覆盖。但 final与 abstract就不能复合来用,因为二者是相冲突的 。
字段与变量字段 (field)是加修饰符的变量,在一个类中通常是声明而来。一个类包含有类字段(变量)与实例字段(变量)。而变量是一个有类型的存储地址。字段与方法都是属于类的成员 。 字段就叫做成员字段,
方法就叫做成员方法。 但方法里的局部变量却不能用修饰符修饰。
若在一个方法的主体里用了字段修饰符,则编译会产生错误。局部变量不能被外界存取 。
变量与字段的不同,由以下的声明格式就更清楚:
变量声明,
< 数据类型><变量名称>〔=<初始值设置 > 〕
字段声明,
< 修饰符><数据类型><数据(字段)名称>〔=<初始值设置 > 〕
修饰符修饰符提供了对类、字段及方法的存取控制 。 下表是所有修饰符的总览。
(package)指的是在没有用任何修饰符的情况下,系统会对变量或方法所默认的 ——表示可被同一 package中的其他类存取 。
修饰符总表修饰符 类 栏 方法接口 注释
(package ) ˇ ˇ ˇ ˇ 可被同一 package 中的其他类存取用此默认修饰符的栏位也叫变量
public ˇ ˇ ˇ ˇ 可被别的 package 中的其他类存取
final ˇ ˇ ˇ final 类不能被扩展( extended
final 方法不能被次类修改
final 栏位就等于是一个常数( constant
abstract ˇ ˇ ˇ Abstract 类必须被扩展
Abstract 方法必须被覆盖( override
pr ivate ˇ ˇ Private 的方法、栏位只能在此类中被看见
protected ˇ ˇ Protected 的方法或栏位能被同一 package
的类看见,以及被其他 package 中该类的次类看见
static ˇ ˇ 定义成类栏位及类方法
synchronized ˇ 在同一时间内,只有一个此种的方法在执行类字段类字段是用 static修饰的字段。此字段属类本身,
意思是只存有一份数据。并没有因实例的产生而被复制。若再加上 fianl修饰符,表示此类字段不能再被更动,这个字段就算是一个常数
( constant)。 惯例上,此字段的命名常用大写字母。
至于此字段的存取,必须以此 [类的实例 ].[类字段 ]的方式来存取,又因每个类的名称一定不同,
故不会有名称相冲突的问题。类字段就相当于
C++ 语言中的全局变量,
类方法类方法也是用 static修饰的方法。类方法也就相当于非对象向导语言中的全局方法、函数,使用类方法有一点要特别注意的,那就是 在类方法中只能使用类字段与类方法,
也就是只能使用 static修饰的字段与方法,而不能使用其余的实例变量。如果要用的话,就需用〔 对象〕,〔数据 〕的方式。这一点是写 Java程序常犯的毛病。在前面轿车的范例中,假如我们想将 System.out.println()的几个方法放在 main()里,则需用下列方式。
public static void main(String args[]){
Sedan sedan=new Sedan();//产 生 实 例
System.out.println("轿车颜色,"+sedan.color);
System.out.println("轿车排档数,"+gearNum);
System.out.println("轿车轮胎型号,"+sedan.tiretype);
System.out.println("轿车排气量,"+sedan.engine);
sedan.shiftgear ();
sedan.brake();
}
假如我们没有用 sedan.[]的方式,则编译会出现错误,
non-static variable color cannot be referenced from a static context
non-static variable tiretype cannot be referenced from a static context
non-static variable engine cannot be referenced from a static context
因这几个变量并没有被声明为 static,所以是实例变量。而 gearNum因为是 static,所以不会产生错误。
实例字段实例字段(变量)指其他不是用 static修饰的字段,可供实例方法使用。实例字段会复制于每一个从类产生的实例中。像前面轿车范例中,所继承的变量,除了
gearNum重新被声明为 static外,其余的均为实例变量
(字段)。
实例方法实例方法同样的也是指那些没有用 static修饰声明的方法。 实例方法可以使用类中所有的字段与方法,也就是类所有的成员,不管他们是 static或不是 static。 类方法与实例方法的不同运用是对象向导程序设计的一大特点 。
实例方法可以使用该实例对象的参考 this。 而类方法就不行。
例如在轿车类的在 shiftgear()方法里,用
public void shiftgear(){System.out.println("轿车换档方式:自排
"+this.gearNum+"档 ");}
是可以的 。
但是用 sedan.gearNum的方式,则出现 error:
public void shiftgear(){System.out.println("轿车换档方式:自排
"+sedan.gearNum+"档 ");}
cannot resolve symbol,variable sedan
这是因为 sedan是被声明在静态(类)方法 main()中,是一种局部变量,只能在 main()里使用。若将 sedan的声明放在程序的最前面变量声明区,就可以用此变量,因为 sedan成了实例变量(一个参考) 。
字段修饰符字段修饰符( field modifiers) 分为两类,
存取性,public,protected,private
存在性,static,final
存取性指的是控制类间的存取,存在性指的是字段本身存在类中的特性 。
字段存取性修饰符
public
此修饰符使字段可被该类的实例或继承该类的子类所存取 。
protected
此修饰符使字段可被该类中的方法,或同一包中的类,
或扩展此类的次类(可存于其他包中),可从其英文意“保护”来领悟。
private
此修饰符使字段受限于该类里面的存取,与其他类无关,扩展的子类亦不能。 此类字段属特殊类内部数据,
用于类内部处理。
字段存在性修饰符
static
此修饰符使字段的存在与该类同等地位。字段数据只有一份,不会因实例的产生而另外产生该字段复制的一个新参考,故将此类字段称为类字段 。
final
此修饰符使字段的值只能被设置一次,不能被其他类或本身类更改。此类字段就相当于一般程序语言中的常数( constant)。 但 const这个关键词仍被保留着,尚未成为正式的 Java语言一部分 。
方法修饰符方法修饰符 (method modifiers)大部分的种类及意义与字段修饰符一样,不过多了一种存在性的 abstract以及线程用的运作性修饰符 ——synchronized。
存取性,public,protected,private
存在性,static,abstract,final
运作性,synchronized
方法存取性、存在性修饰符方法存取性修饰符与字段修饰符一样有 3个。其功能与字段的完全一样。故不再赘述 。
方法存在性修饰符
static
此修饰符会使方法成为唯一的。与类同地位。不会因实例的产生而受影响。 static方法在使用上,有几点注意事项,
1,static方法只能使用 static字段。 否则编译会出错。
像在 main()里,方法通常是用 public static来修饰,所以只能用 static的字段。
2,一类的 static字段与 static方法,可直接用该类的名称,按下面方法来取用,
〔类名称〕,〔静态字段〕
〔类名称〕,〔静态方法〕
〔类名称〕,〔静态字段〕,〔静态方法〕
例如,
color=Color.blue;
String password=System.getProperty(“user.password”);
System.out.println();
方法存在性修饰符
abstract
抽象方法存于抽象类中,并不建置程序代码,乃是留给继承的子类来覆盖。声明抽象方法时,并不用写出大括号{},否则编译会出错。请参见前面汽车抽象类范例。
final
被声明为 final的方法不能被其他类变更方法里的程序内容。连继承的子类也不能 。
方法运作性修饰符
synchronized
此方法修饰符用于同步化监控处理。一群被
synchronized的方法,一次只能被一个线程来使用,就好像一个停车场内有多部车辆,但只有一把共享的钥匙。如果有几个驾驶( thread)
要使用车辆,就只有一个人可以进去使用,而其余人必须等候,俟该名驾驶返回并归回钥匙后,下一位才能进去使用 。
静态初始者 (static initializer)
静态初始者 (static initializer)是一种可一在类加载时,便可做一些起始动作的一段程序区块。它是由 static加上一组大括号所组成 。
static{
程序区块

它与构造函数不同。构造函数需要有该类的实例产生时,才会被调用到。但静态初始者则是在此类加载时,便会动作。静态初始者也与构造函数一样,在同一个类中可有多个。静态初始者也与类方法一样,不能使用实例变量及方法,也不能使用 this关键词。
范例 9_4:我的车类 _1
import java.awt.Color;
import java.io.*;
public class MyCar_1 extends Sedan{
static private Color color;
static private String input;
static {//静态初始者
System.out.println("请选择您所喜爱的汽车颜色,");
System.out.println("1.白色 2.深灰色 3.红色 4.橘色 ");
BufferedReader readin=new BufferedReader(new InputStreamReader(System.in));
try{
input=readin.readLine();
}catch(IOException ioe){System.out.println(ioe);}
}
public MyCar_1() {
System.out.println("输入的值是,"+this.input);
switch(Integer.parseInt(this.input)){
case 1,color=Color.white;
break;
case 2,color=Color.darkGray;
break;
case 3,color=Color.red;
break;
case 4,color=Color.orange;
break;
}
}
public static void main(String args[]) {
MyCar_1 mycar_1=new MyCar_1();
mycar_1.equipment();
mycar_1.shiftgear ();
mycar_1.brake();
}
public void equipment(){
System.out.println(“我的爱车排档数,"+this.gearNum);
System.out.println("我的爱车颜色,"+this.color);
System.out.println("我的爱车轮胎型号,"+this.tiretype);
System.out.println("我的爱车引擎排气量,"+this.engine);
}
public void shiftgear(){//换档 -覆盖的新方法
super.shiftgear();
System.out.println("我的爱车换档方式:自排 "+this.gearNum+"档 ");}
}
执行结果:
请选择您所喜爱的汽车颜色:
1.白色 2.深灰色 3.红色 4.橘色
2
输入的值是,2
我的爱车排文件数,5
我的爱车颜色,
java.awt.Color[r=64,g=64,b=64]
我的爱车轮胎型号,BridgeStone185ST
我的爱车引擎排气量,1598.5
轿车换文件方式:自排 5文件我的爱车换文件方式:自排 5文件水压式煞车系统内部类 ( inner class)
内部类( inner class) 是存在于一类中的类。它是 Java语言 1.1版后的一种强大且优异的功能。内部类亦有人称为嵌套类( nested classes)。 内部类有 4种,
1,静态成员类 (static member class)
指在一类中被定义且用 static修饰的类(也可是接口),其地位就像是一个类方法一般,
可存取所在之类的静态成员(包括静态字段,静态方法)。因其地位存在于内部顶层顶层的位置,故又称嵌套顶层类( nested top-level classes)。 接口只能被定义成类中的一种静态成员,而不能为非静态成员 。
2,成员类 (member class)
指在一类中被定义的类,但不是用 static修饰的类,其地位就像是一个实例方法一般,
可存取所在之类的所有字段与方法 。
3,局部类 (local class)
指存在于一程序区块里的内部类。其地位就像局部变量一样,只在该区块内被使用。
但接口不能被定义成局部类。
4,匿名类 (anonymous class)
指没有名称的局部类。常用于事件处理时,简化事件倾听者及事件处理者的声明。接口同样不能被定义成这种类。
import java.awt.*;
import java.awt.event.*;
import java.applet.*;
import javax.swing.*;
public class innerClassTest extends JApplet
{
JButton jButton1=new JButton();
static int a=333;
private char b='B';
static interface staticInterface{};//静态成员接口
static class staticMemberClass{};//静态成员类
public class memberClass{};//成员类
public void init() {
String c ="I love Java";
class localClass{};
this.getContentPane().add(jButton1,BorderLayout.CENTER);
jButton1.addActionListener(new java.awt.event.ActionListener() {//匿名类
public void actionPerformed(ActionEvent e) {
jButton1_actionPerformed(e);
}
});
}
void jButton1_actionPerformed(ActionEvent e) {
}
}
接口 ( Interface)
在第 4章中,我们已对接口稍有概念上的介绍。接口的用处,是要解决多重继承的问题。而在一个接口里,可以定义一些字段及声明方法,但并不实现这些方法。定义一个接口,就有点像定义一个抽象类一样,所声明的方法不必加 abstract,亦被视为 abstract。 而字段只能用 static与 final来修饰。
例如,我们将第 4章中的汽油车接口定义如下:
public interface Gasoline
{
public static final String FUEL="gasoline(汽油) ";//定义一个最终字段
public void refuel();//声明一个方法
}
而我们另外将前面的轿车类稍加修改,来实现这个接口。就可以运用此接口中所定义的字段及方法 。
范例 9_5:轿车类 _1
import java.awt.Color;
public class Sedan_1 extends Car implements Gasoline{
static int gearNum=5;//声明 gearNum为类栏
public Sedan_1() {//构造函数
tiretype="BridgeStone185ST"; //轮胎型号
engine=1598.5f;//排气量
}
public static void main(String args[]){
Sedan_1 sedan_1=new Sedan_1(); //产生实例
sedan_1.equipment();
sedan_1.shiftgear ();
sedan_1.brake();
sedan_1.refuel();
}
public void equipment(){
System.out.println("轿车颜色,"+color);
System.out.println(“轿车排档数,"+gearNum);
System.out.println("轿车轮胎型号,"+tiretype);
System.out.println("轿车排气量,"+engine);
System.out.println("轿车燃料,"+FUEL); //用到了接口中的字段
}
public void shiftgear(){System.out.println("轿车换档方式:自排 "+ gearNum+"档 ");}//换档
public void brake(){System.out.println("水压式煞车系统 ");} //煞车
public void aircon(){}; //开冷气
public void headlight(){};//开大灯
public void refuel(){System.out.println("轿车要加 "+FUEL);} //覆盖接口所声明的方法
}
執行結果:
轿车颜色,null
轿车排文件数,5
轿车输胎型号,
BridgeStone185ST
轿车排气量,1598.5
轿车燃料,gasoline(汽油)
轿车换文件方式,自排 5文件水 压式煞车系统轿车要加 gasoline(汽油 )
包 (Package)
包是一种将相关类及接口组织起来的集合体。目的是为了类及接口名称的管理与存取保护。这样对程序设计人员很方便,可以很容易地找到所要的类 。
每一个包都拥有一个完整的命名空间( name space),
使得包内的类名称,不会与其他包中的类名称相冲突。
并且我们也可限制一个包的对内与对外的存取权限。
建立及引用一个包至于如何建立一个包呢?这很简单,只要将 package这个关键词放在类或接口的类或接口声明前,并选定一个包名称,这样所有用到此包名称的类及接口就成了此包的成员。如:
package OOP;
import java.awt.Color;
public abstract class Car {

}
而要引用一个包,是用 import这个关键词来引入某包,就如
import java.awt.Color;就是把 java.awt包里的 Color类引用进来,如果要引用整个包内所有的类及接口,就用 *号:
import java.awt.*;
package的命名接着,我们来考虑一下 package的命名问题。因为全世界的 Java程序设计师很多,所以很可能会发生使用同样的包名称。那怎么办呢?有一个好的解决方法就是用 package的习惯用法,
com.company.region.package
[com.公司名称,地区,包名称 ]
这样每一家公司,或是同一公司的不同地区或部门,所选用的包名称就不会混淆。例如下面两个 Borland公司所出的 Jbuiler中所内建的包,
com.borland.dbswing
com.borland.jdbc
引用一个包中的类而当我们在一个类中引用一个包中的类时,可用两种方式:
1,类长名( long name) ——即加上包名称的类名,如:
com.borland.jdbc.connection
类长名也可使用在建立一个新的实例上,如:
com.borland.jdbc.connection conn=new com.borland.jdbc.connection();
2,类短名 ( short name) ——
需在类程序最前面引入包,然后使用该类名,如:
import java.awt.*;

Color color1=new Color();