Java最新实用教程第五章 类的重用
Java最新实用教程
2009年 7月 27日星期一 2
学习目的:
掌握 类的继承 及 Object类
掌握 终结类及终结方法
掌握 抽象类
理解 类的组合
熟悉常用内置包
学习重点:
掌握 类的继承与组合
掌握 抽象类及内置包
Java最新实用教程
2009年 7月 27日星期一 3
第 五 章 类的重用本章提要:
类的继承 及 Object类
终结类及终结方法
抽象类
继承与组合
常用 包
Java最新实用教程
2009年 7月 27日星期一 4
5.1 类的继承
5.1.1继承的概念继承是由已有的类创建新类的机制,是面向对象程序设计的重要特征之一 。 一个新类可以从现有的类中派生,这个过程称为类继承 。 派生出的新类称为已有类的子类,已有类称为父类或超类 。
继承性很好地解决了软件的可重用性问题 。 通过继承,一个类可以用有已由类的所有功能 。 因此,只需指明新类与现有类的不同,即增加自己的属性和方法即可 。
java要求每个类都有父类,当没有显式继承父类时,父类隐含为 java.long包中的 object类 。 一个父类可以同时拥有多个子类,此时这个父类实际上时所有子类的共有属性即方法的
Java最新实用教程
2009年 7月 27日星期一 5
集合,而每一个子类则是父类的特殊化,是在公共属性的基础上的功能及属性的扩展和延伸 。
java不支持类的多重继承,只支持可的单继承,即每个子类只能有一个直接父类,类的层次结构为树状结构,object类为树的根节点 。
特别要注意的是:通常根据应用定义自己的类层次,在定义类的层次结构中,父类与子类的关系是一般于特殊的关系,而不是整体与部分的关系,如图 5-1,5-2所示 。
图 5-1 类层次结构图举例图 5-2 错误的类层次结构图
Java最新实用教程
2009年 7月 27日星期一 6
与多重继承 ( 相应的类结构为网状结构 ) 相比,单继承比较简单,掌握,控制起来相对容易 。 Java虽然只支持单继承,但可通过接口来实现多继承功能 。
采用继承的机制来组织,设计系统的类,可以提高程序的抽象程度,使之更接近于人类的思维方式;采用继承还可以使编写的程序结构清晰,节省了编程时间;同时因为代码的编写量减少,也降低了程序维护的工作量 。
5.1.2继承的语法在 java语言中,子类对父类的继承是在类的声明语句中实现的
。 前面提到类声明语句如下:
[类修饰符 ] class 类名 [extends 父类名 ] [implements [,接口名 ]
{
//类体
}
Java最新实用教程
2009年 7月 27日星期一 7
其中,关键字 extends说明了此处定义的,类名,继承了,父类名,指定的类中的属性和行为 。
例 5.1:设计人员类 。
分析:在一个公司中,有普通职工 ( Employees) 和管理者 (
Managers) 两类人员 。 对这两类人员的属性和行为进行分析可知,管理者是职工中叫特殊的一部分 。 可以将类的关系设计如下:
父类 Employee可能有的属性包括:
员工号 ( id),姓名 ( name),地址 ( address),电话 (
phone),职责 ( task) 。
管理者 ( Manager) 除具有以上所有属性外,还包括特殊的属性,可以将 Managers设计为子类,它新具有的属性可以是:所管理的职员 ( list) 。
依据分析,可设计如下两个类 。
Java最新实用教程
2009年 7月 27日星期一 8
父类:
public class Employee
{//成员变量
int id;
String name,phone,task;
//方法略
}
子类:
public class Manager extends Employee
{//成员变量
String list;
//方法略
}明显地,子类继承了父类的属性,所以简化了子类的定义过程
,提高了程序的重用性。
Java最新实用教程
2009年 7月 27日星期一 9
另外,子类继承父类时,父类的所有成员不一定都可以被继承
,也就是说,继承受一些条件的限制,如父类成员前的访问控制修饰符、子类与父类所在包的位置关系等因素。
前面第三章中曾经提到,访问控制修饰符主要在继承或包之间的成员访问时进行权限的控制:
public公有访问控制符 。 该修饰符修饰的变量称为公有成员,如果公有成员又在一个公有类中,那么这个成员将能被所有的类访问 。
protected 保护访问控制符 。 该修饰符修饰的成员能被所在的类自身,同一个包中的所有类,其他包中的该类的子类所访问 。 能被其他包中的该类的子类访问是 protected成员的重要特征 。
friendly 默认访问控制符 。 如果成员前没有访问控制符
,则默认为 friendly类型,称为友好成员 。 此类变量或方法可以被
Java最新实用教程
2009年 7月 27日星期一 10
同一个包中的其他类所访问 。
private 私有访问控制符 。 该修饰符修饰的成员仅仅能被所在类自身访问,任何其他类都不可访问,包括该类的子类 。
以上修饰符不但可以控制变量的访问情况,还可以用同样的规律控制类的成员方法的访问权限 。
1,公有变量及方法的继承还以上面的两个类为例,假设父类还有一个方法 getTask。
例 5.2:将父类成员声明为 public,验证子类的继承效果 。
父类:
public class Employee
{//成员变量
int id;
String name,phone,task;
public String getTask()//公共成员方法
{
return task;
}
}
Java最新实用教程
2009年 7月 27日星期一 11
子类:
public class Manager extends Employee
{//成员变量
String list;
//方法略
}
此时如果在另一个类中创建 Manager类的实例 。
子类实例应用:
public class Jerry
{
public static void main(String args[])
{
Manager jerry = new Manager (); //创建 Manager实例
/*因为 Manager继承自 employee,所以 Manager类中包含继承来的 task属性以及共公的 getTask
方法 。 jerry 作为类的实例,也就可以访问相应的属性和方法 */
jerry.task="管理员工 ";
System.out.println("jerry的工作是,"+jerry.getTask());
}
}
执行结果,Jerry的工作是:管理员工。
Java最新实用教程
2009年 7月 27日星期一 12
2,保护( protected) 及友好成员的继承如果把上例中的父类的属性和方法都用 protected修饰,即如下定义:
public class Employee
{//成员变量
int id;
protected String name,phone,task; //保护类型变量
protected String getTask()//成员方法
{
return task;
}
}
此时这样的改变对程序 Jerry.java的执行没有任何影响,因为
protected类型成员可以被 Employee类所在同一个包中的所有类及其他包中的子类访问或继承,而此处定义的类都在当前包中
,即在同一个包中。
另外,细心的读者可能会发现,在例 5.2中虽然方法被声明为
public,但变量 task是没 有修饰符的,即 task等属性为默认的
Java最新实用教程
2009年 7月 27日星期一 13
友好成员变量,但子类同样能够访问到。这是因为此类变量或方法可以被同一个包中的其他类所访问,对于 Employee类来说,同一个包中的所有类当然也包括子类 Manager。 读者可以自行验证。
3,私有成员的继承 。
例 5.3:同样的子类和子类实例,只把父类 Employee中的属性即方法该为 private修饰 。
public class Employee
{//成员变量
int id;
private String name,phone,task; //私有变量
private String getTask() //私有成员方法
{
return task;
}
}
编译时出现如,task has private access in Employee”之类的错误提示 。
Java最新实用教程
2009年 7月 27日星期一 14
分析编译出错的原因可知,因为 Jerry类中建立了 Manager类的实例,而 Manager类是继承自 Employee类,但由于 Employee类中的属性 task,方法 getTask()均为 private类型,所以 Manager类继承不到,即没有访问权限。
事实上,对于私有成员,只有在定义私有成员的类自身中创建该类的对象才能访问。私有成员的访问限制虽然较大,但对于类的封装即模块化设计来说具有重要的意义。为了实现良好的封装性,通常将类的成员变量声明为 private,再通过 public的方法来对这个变量进行访问。
例如,上面 Employee类中的方法该为 public就是比较好的一种选择。
5.1.3 隐藏和覆盖隐藏和覆盖是指子类对从父类继承来的属性变量及方法可以重新加以定义 。
Java最新实用教程
2009年 7月 27日星期一 15
1,属性的隐藏子类对从父类继承来的属性变量重新加以定义,则继承父类的属性被隐藏。如:
class A{
int i = 3;

}
class B extends A{
float i = 3.14f;

}
这时,子类中声明了与父类同名的属性变量 i,即出现了子类变量对同名父类变量的隐藏。这里所谓隐藏是指子类拥有了两个相同名子的变量,一个继承自父类,另一个由自己声 明;当子类执行继承自父类的操作时,处理的是继承自父类的变量,而
Java最新实用教程
2009年 7月 27日星期一 16
当子类执行它自己声明的方法时,所操作的就是它自己声明的变量,而把继承自父类的变量隐藏起来了。
这样就出现一个问题,也就是如何在子类自己定义的方法中调用父类的变量? 在子类中使用关键字 super即可,格式为:
super.变量名例 5.4:一个属性隐藏简单的例子 。
class A
{
int i = 3;
public void printi()
{
System.out.println("父类方法 i="+i);
}
}
class B extends A
{
float i = 3.14f; //属性覆盖
public void printfi()
{
Java最新实用教程
2009年 7月 27日星期一 17
System.out.println("i="+i);
System.out.println("父类中 i="+super.i); //调用被覆盖的父类变量
}
}
public class C
{
public static void main(String args[])
{
B b=new B();
b.printfi();
b.printi();
}
}
执行结果如图 5-3。
图 5-3 属性隐藏
Java最新实用教程
2009年 7月 27日星期一 18
另一个要注意的问题是:子类并不能继承父类的静态属性,但可以对父类中的静态属性进行操作。
1,方法的覆盖如果子类不需要使用从父类继承来的功能,则可以声明自己的方法。子类在声明时,可能使用到和父类方法名及参数表完全相同的方法,但却执行不同于父类的功能。这种情况称为方法覆盖。
一般在下面三种情况下需要使用覆盖:
子类中实现与父类相同的功能,但采用不同的算法或公式。如:
class Profit
{
float f(float x,float y)
{
return x*y;
}
}
class P extends Profit
Java最新实用教程
2009年 7月 27日星期一 19
{
float f(float x,float y)
{
return x+y;
}
}
在名字相同的方法中,要做比父类更多的事情。
在子类中需要取消从父类继承的方法。
注意:在子类中重写父类已有方法时,应保持与父类完全相同的方法名,返回值和参数列表,即相同的方法签名,并且不允许降低方法的访问权限。
5.1.4继承时的构造方法构造方法是中特殊的方法,有继承时构造方法遵循以下原则:
子类不能从父类继承构造方法 。
好的程序设计方法是在子类的构造方法中调用父类的某一个构造方法 。
Java最新实用教程
2009年 7月 27日星期一 20
super关键字也可以用于构造方法中,其功能为调用父类的构造方法 。
如果在子类的构造方法的声明中没有明确调用父类构造方法,则系统在执行子类的构造方法时会自动调用父类默认的构造方法 ( 即无参数的构造方法 ) 。
如果子类的构造方法的声明中调用父类的构造方法,则调用语句必须出现在子类构造方法的第一行 。
例 5.5:有继承时的构造方法 。
public class Person
{
protected String name,phone,address;
public Person()
{
this(" "," "," ");
}
public Person(String aName,String aPhone,String anAddress)
{
name = aName;
Java最新实用教程
2009年 7月 27日星期一 21
phone = aPhone;
address = anAddress;
}
}
class Employees extends Person
{
protected int id;
protected String workphone;
public Employees()
{
//此处隐含调用构造方法 Person()
this(0,"");
}
public Employees(int aId,String aPhone)
{
//此处隐含调用构造方法 Person()
id=aId;
workphone=aPhone;
}
}
class Professor extends Employees
{
protected String research;
Java最新实用教程
2009年 7月 27日星期一 22
public Professor()
{
super();
research="";
}
public Professor(int aId,String aPhone,String aResearch)
{
super(aId,aPhone);
research=aResearch;
}
}
注,this( … ) 代表当前类中的其它构造方法 。 如类 Employees
中 this(0,"")表示调用了方法 Employees(int aId,String aPhone)。
5.1.5应用举例类继承的层次结构设计取决于具体的应用 。
例 5.6:类的继承设计假设在一个公司管理系统,前面提到有普通职工 ( Employee
Java最新实用教程
2009年 7月 27日星期一 23
) 和管理者 ( Manager) 两 类人员 。 对这两类人员的属性和行为进行分析可知,管理者是职工中较特殊的一部分 。 可以将类的关系设计如下:
父类 Employee可能有的属性包括:
员工号 ( id),姓名 ( name),地址 ( address),电话 (
phone),职责 ( task) 。
管理者 ( Manager) 除具有以上所有属性外,还包括特殊的属性,可以将 Managers设计为子类,它新具有的属性可以是:所管理的职员 ( list) 。
但除了这两类人员外,还会有顾客 ( Customer) 类,下面主要考虑怎样合理地表示顾客类 。 一个顾客可能有的属性包括:姓名 ( name),地址 ( address),电话 ( phone) 。 还可以有行为,shop等 。
若将以上三种人员类的层次关系表示为如图 5-4所示,明显不
Java最新实用教程
2009年 7月 27日星期一 24
关键的原因是顾客不总是职员,职员一般情况下也不是顾客,
他们之间没有继承关系 。 此时在对比顾客类和普通员工类发现
,顾客类有的属性,普通员工也都有,只不过普通员工类增加了新的属性 。 此时是否就可以说普通员工类继承自顾客类呢?
有时考虑到顾客类的可能存在某些属性是普通员工所不具有的
,如购物清单 ( shopList),可以把顾客,普通员工在进行抽象概括,声明一个新的类 Person类 。 总的类层次如图 5-5。
最后,每个类的属性清单如下所示:
Person,name,address,phone。
Employee,id,task。
Manager,list。
Customer,shopList。
然后再使用相同的思考方式来抽取共同的行为,将其放到合适的类中即可 。
Java最新实用教程
2009年 7月 27日星期一 25
图 5-4 不合理的类层次 图 5-5 公司管理系统类层次
5.2 Object类
java类库是由系统提供的已实现的标准类的集合,是 java编程的
API( application program interface),它可以帮助开发者方便
,快捷的开发 java程序 。 这些系统定义好的类根据实现的功能不同,可已划分成不同的集合,每个集合是一个包,成为类库
。 java的类库大部分是由它的发明者 -----SUN公司提供的,这些类库称为基础类库 。
Java最新实用教程
2009年 7月 27日星期一 26
Object类是 java程序中所有类的直接或间接父类,也是类库中所有类的父类,处在类层次的最高点 。 所有其他类都是从
object类派生出来的,所以 object包含了所有 java类的公共属性,其构造方法是 object(),object类的其他较为主要的方法有:
public final class getClass() 用来获取当前对象所属类的信息,返回 Class对象
public String toString() 用来获取当前对象本身的有关信息,按字符串对象返回
public Boolean equals(Object obj) 用来比较两个对象是否为同一对象,是则返回 true
public void finalize() throws Throwable 用来回收定义当前对象时所用资源在此重点介绍 equals方法的使用

Object类中的 equals()方法及对象相等与同一的判断 。
Java最新实用教程
2009年 7月 27日星期一 27
前面学过了判断两个变量是否相等的逻辑等运算符,==”,对象也是一种变量,也可以用等号来判断两个对象的相等关系。
但是,==”和 equals()有什么区别呢?首先要区分相等与同一概念。
如果两个对象具有相同的类型及相同属性值,则这两个对象相等;如果两个对象引用指向的是同一个对象,则称这两个对象引用变量同一 。
因此,如果两个对象同一,这两个对象肯定相等;反之,
如果两个对象相等,则不一定同一 。,==” 判断两个对象是否同一的关系,equals()判断是否相等的关系 。
由于 Object是类层次结构中的树根节点,所有的其他类都是 Object类的子类,因此,所有的其他类都继承了 equals()
方法 。
相等与统一的判断示例:
Java最新实用教程
2009年 7月 27日星期一 28
class Student
{
String name;
int age;
}
public class Test
{
public static void main(String[] args)
{
Student p=new Student();
Student q=new Student();
Student r=p;
p.name="xyz";
p.age=13;
q.name="xyz";
q.age=13;
System.out.println(p==q); //p和 q不是同一个,应该显示 false
System.out.println(p.equals(q));//p和 q不是同一但属性相等,应显示 true
System.out.println(p==r); //p和 r是同一,应显示 true
}
}
Java最新实用教程
2009年 7月 27日星期一 29
在上面的程序中,使用从 Object类继承来的 equals方法判断 p和 q两个对象的属性值是否相等 。 但出乎意料的是:
p.equals( q) 结果显示,false”。
下面来看 Object类中的 equals()方法的定义,其定义如下

public Boolean equals(Object x)
{
return this==x;
}
从 equals的定义可知,其功能还是比较两个对象是否为同一个对象,即对象的地址是否相同,而不是判断两个对象的属性值是否相同 。
判断两个对象的属性值是否相同,需要在当前类(假设为
Student) 中声明中重写 equals方法,重写时应按照方法重写的规则进行:
Java最新实用教程
2009年 7月 27日星期一 30
public boolean equals(Object obj)
{
Student st=null;
if(obj instanceof Student) //首先判断 obj是否为当前类的对象 。
st = (Student)obj;
else
return false;
if(… ) //属性判断表达式
return true;
else
return false;
}
上述例子改进后,可写为:
例 5.6,equals方法综合应用示例 。class Student
{
String name;
int age;
public boolean equals(Object obj)
{
Student st=null;
if(obj instanceof Student)
Java最新实用教程
2009年 7月 27日星期一 31
st = (Student)obj;
else
return false;
if(st.name==this.name && st.age==this.age)
return true;
else
return false;
}
}
public class Test
{
public static void main(String[] args)
{
Student p=new Student();
Student q=new Student();
Student r=p;
p.name="xyz";
p.age=13;
q.name="xyz";
q.age=13;
System.out.println(p==q);
System.out.println(p.equals(q));
System.out.println(p==r);
}
}
运行结果如图 5-6。
图 5-6 对象的比较
Java最新实用教程
2009年 7月 27日星期一 32
5.3 终结类与终结方法
5.3.1终结类如果一个类被 final修饰或限定,说明这个类不可能有子类,即
final类不能有派生类 。 如果把有继承关系的类组织成一棵倒状的树,所有类的父类是树根,每个子类是一个分支,则被声明为 final的类只能是这棵树上的叶节点 。
被声明为 final的类通常是一些固定作用,用来完成某种标准功能的类,不能被继承以达到修改的目的,如 java 系统的
java.long.String,java.long.Math,java.net.InetAddress。 在 java程序设计中,当引用一个类或其对象是实际引用的既可能的确是这个类或其对象本身,但也可能是这个类的子类及子类对象,
即具有一定的不确定性 。 将一个类声明为 final,则可以将它的内容,属性和功能固定下来,与它的类名形成稳定的映射关系
,从而保证引用这个类时所实现功能的稳定性 。
Java最新实用教程
2009年 7月 27日星期一 33
Final类的存在有以下两个理由:
安全方面:黑客用来搅乱系统的一个手法是建立一个类的派生类,然后用它们的类代替原来的类 。 这个派生类看起来和原来的类没有什么区别,但实际上它和原始类做完全不同的事情,可能引起一系列的系统损坏或机密泄漏 。 为了避免这种情况发生,可以声明此类为 final类,从而不能派生任何子类

在 java.long包中,String类就是由于这个原因而声明为 final类的
。 这个类对编译器和解释器的操作非常重要,以至于 java必须保证一个方法或对象无论何时使用 String时,String必须来自
java.long包,而非其他的包 。 这就保证了所有的字符串没有奇异的,不一致,不想要或不可预知的属性 。
如果编译一个 final类的子类,编译器将输出一个错误信息,编译不通过 。 此外,字节校验码通过检查一个类不是 final类的子类来确保颠覆不会发生在字节码层次上 。
设计方面从面向对象设计的角度考虑,如果某些对象
Java最新实用教程
2009年 7月 27日星期一 34
概念上讲不应该有任何派生类,则最好声明该类为最终类 。
声明一个类为最终类,须在类名前加修饰符 final。 格式如下:
final class 类名
{

}
5.3.2终结方法
final修饰符修饰的方法是功能和内部语句不能被更改的最终方法,即不能被当前类的子类重载的方法。在面向对象程序设计中,方法的重写使得子类可以对父类中的方法进行更新。这种方法固然有其自身优势,但对于一些比较重要且不希望子类进行更改的方法,可以使用 final关键字对成员方法进行声明,
这样,该类的子类就不能再重新声明与此方法同名的方法了,
Java最新实用教程
2009年 7月 27日星期一 35
此时子类只能使用从父类的继承来的方法 。 通过这种方法,固定了某些方法所对应的操作,可以防止子类对父类关键方法的错误重写,增加了代码的安全性和正确性 。
除了安全方面的考虑,将方法声明为终结方法的另一个原因是可以提高类的运行效率 。 通常当 java运行环境 ( 如 java解释器
) 运行方法时,它将首先在当前类中查找该方法,接下来在其超类中查找,并一直沿类层次向上查找,直到找到该方法为止
。 这提供了灵活性和开发工作的容易程度,但代价是速度的降低 。
如果方法是 final的,java编译器可以将该方法的可执行字节码直接放到调用它的程序中,毕竟该方法不会被子类覆盖而发生变化 。 Java类库将很多常用的方法声明为 final,这样,当程序调用它们时,执行速度将更快 。
首次开发一个类时,没有理由使用 final。 然而如果要使其
Java最新实用教程
2009年 7月 27日星期一 36
执行速度更快,可已将一些方法修改为 final。 是否将一个方法声明为 final,要根据需要确定 。
由于此类问题应用不是很复杂,读者可以自行验证 。
注意,final关键字一般作为非访问控制修饰符出现,可以和访问控制修饰符以及静态修饰符一起用,如 public static final
成员名 。 即 final不仅可以修饰方法,也可以修饰属性,被 final
修饰的属性是不能再被修改,即为常量 。
5.4 抽象类抽象类就是不能使用 new运算符进行实例化的类,即没有具体实例对象的类。
抽象类也是类,可以包含常规类能够包含的任何东西,如构造方法即其他功能方法,因为子类可能需要继承这些方法。抽象类也可以包含抽象方法,这种方法只有方法的声明,没有方法的实现。这些抽象方法将在抽象类的子类中被实现。
Java最新实用教程
2009年 7月 27日星期一 37
如果一个抽象类除了声明了抽象方法外什么都没有,则使用接口更合适,接口将在下一章介绍。
5.4.1抽象类的声明当一个类被声明为抽象类时,要在这个类前加关键字 abstract。
抽象类的声明格式为:
public abstract class 类名
{
//类体
}
java中使用抽象类的原因有如下五点:
抽象类代表类层次结构中较高层次的概括,抽象类的作用是让其他的类来继承它的抽象化的特征。
抽象类可以包括被它的所有子类共享的公共行为。
抽象类可以包括被它的所有子类共享的公共属性。
Java最新实用教程
2009年 7月 27日星期一 38
程序中不能直接用抽象类作为创建对象的模板。
在用户生成实例时强迫用户生成更具体的实例,保证代码的安全性。
5.4.2抽象方法作为类修饰符,abstract声明了一种没有具体对象的,出于组织概念的层次关系需要而存在的抽象类。 abstract还可以作为方法修饰符,它声明了一种仅有方法头,而没有具体方法体和操作实现的抽象方法,作用是为该类的子类声明了一个方法的接口标准。
抽象方法声明的格式为:
public abstract [数据类型 ] 方法名( [参数 1[,参数 2,参数
3……] ] );
抽象方法的方法体是由当前类的不同子类在它们各自的类声明中完成的。也就是说,各类的子类在继承父类的抽象方法
Java最新实用教程
2009年 7月 27日星期一 39
后,再分别重写它,形成若干个名字相同,返回值类型相同,
参素列表也相同但具体功能有一定差别的方法。
使用抽象方法须注意:
一个抽象类的子类如果不是抽象类,则它必须为父类中的所有抽象方法书写方法体,即重写父类的所有抽象方法 。
抽象方法所在类必定为抽象类,即抽象方法必在抽象类中 。
抽象类中的方法不一定都是抽象方法 。
抽象方法中虽没有方法体的实现,但却是必需的,因为抽象方法有以下优点:
这种方法可以隐藏具体的细节信息,是调用该方法的程序不必过分关注该类和它的各子类的内部状况 。 由于所有的子类使用的都是相同的方法头,而方法头里包含了调用该方法的程序语句所需要了解的全部信息 。
Java最新实用教程
2009年 7月 27日星期一 40
这种方法强迫子类完成指定的行为,抽象类的所有非抽象子类都必须完成其父类中声明的抽象方法 。 抽象类声明抽象方法
,通常用来规定其子类需要用到的,标准,行为 。
例 5.7:分析下面程序的输出结果,熟悉抽象类和抽象方法的定义和使用 。
abstract class Shape
{
public abstract double 求面积 ();
}
class 梯形 extends Shape
{
double a,b,h;
梯形 (double a,double b,double h)
{
this.a=a;this.b=b;this.h=h;
}
public double 求面积 ()
{
return((a+b)*h*0.5);
}
}
Java最新实用教程
2009年 7月 27日星期一 41
class 圆形 extends Shape
{
double r;
圆形 (double r)
{
this.r=r;
}
public double 求面积 ()
{
return(Math.PI*r*r);
}
}
public class Abstractshape
{
public static void main(String args[])
{
Shape trapzoid = new 梯形 (2.0,7.0,10);
Shape oval = new 圆形 (2.0);
System.out.println("梯形面积 "+trapzoid.求面积 ());
System.out.println("圆形面积 "+oval.求面积 ());
}
}
运行结果如图 5-7。
Java最新实用教程
2009年 7月 27日星期一 42
图 5-7 抽象类和抽象方法
5.5 类的组合面向对象编程的一个重要思想就是用软件对象模拟现实世界的对象 。 现实世界中,大多数对象由比较小的对象组成 。 例如自行车就是一个对象,它由车架,车轮,转动装置,车把等对象组成,有些部分还可以再继续划分成更小对象,如车轮是由车圈,外胎,内胎等组成 。
与现实世界中的实体对象一样,软件中的对象也常常是有更小的对象组成 。 java中的类中可以有其他类的对象作为成员,这便是类的组合 。
Java最新实用教程
2009年 7月 27日星期一 43
5.5.1组合的语法组合是实现复用最简单的方法,它的语法比较简单,只要把已存在的类的对象放到新类中即可 。 可以使用,has a”语句来描述这种关系 。 格式如下:
class Cooker
{//类语句
}
class Refrigerator
{//类语句
}
class Kitchen
{ //类 Kitchen中有 Cooker类和 myRefrigerator类的实例
Cooker myCooker;
Refrigerator myRefrigerator;

}
如前面的例子 5.6,5.7等都是这样的应用 。
5.5.2组合与继承的比较
Java最新实用教程
2009年 7月 27日星期一 44
一般来讲,组合和继承的差别就是,has a”和,is a”关系的差别
,例如苹果是水果,应该使用继承的关系建立苹果类和水果类的关系模型;而人有名字,就应该用组合建立人这个类和名字类的关系模型 。 但有时候,继承和组合的选择不是很明确,可能两种方式都可以实现同样的效果 。 此时该如何选择呢?
如果只想在当前类中使用其他类的某些特性,而不想具有其他类的接口,此时考虑使用组合 。 特别是希望在当前类中使用某个类的 private成员,只能通过组合即嵌入一个对象来实现 。
如果想让当前类具有某个类的特性,但此类直接用时有若干困难或不能直接用时,就只能考虑继承 。
5.5.3组合与继承的结合许多时候要求将组合与继承两种方式结合起来使用,从而创建一个复杂的类,同时进行必要的初始化工作 。 如小程序必须
Java最新实用教程
2009年 7月 27日星期一 45
继承类 Applet,但小程序中从来不是只有 Applet类的成员,它需要组合大量的其他的类,来完成一定的功能。
例 5.8:希望把 X类的某个属性或方法的返回值用小程序显示。
import java.applet.*;
import java.awt.Graphics;
public class A extends Applet
{
public void paint(Graphics g)
{
X b=new X();
g.drawString(b.…,,50,80); //引用类 X的实例进行输出
}
}
class X
{

}
Java最新实用教程
2009年 7月 27日星期一 46
5.6 包的应用利用面向对象技术开发一个实际的系统时,通常需要设计许多类共同工作 。 由于 java编译器为每一个类生成一个字节码文件
,同名的类就有可能发生冲突 。 为了解决这一问题,java提供包来管理类名空间 。 正如在操作系统中,目录用来组织文件并设置权限,java利用包来组织相关的类,并控制访问权限 。
包是一种松散的类的集合 。 一般不要求处于同一包中的类有明确的相互关系,如包含或继承等 。 但是由于同一包中的类默认情况下可以互相访问,所以为了方便编程和管理,通常把需要一起工作的类放在一个包中 。 利用包来管理类,可已实现类的共享与复用 。
5.6.1Java基础类库概述
java提供了用于语言开发的类库,称为 java基础类( java
foundational class,JFC) 库,也称为应用程序编程接口
Java最新实用教程
2009年 7月 27日星期一 47
( application programming interface,API),分别放在不同的包中 。 Java提供的包主要有 java,long,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等,下面对常用的包进行介绍 。
所有 java程序默认引入 java.long包,除 java.long包外,如果还要用到其他包中的类,则需引入包,如小程序均需引入 java.applet
包:
import java.applet.*;
“.*”代表引入包中的所有类 。
1,语言包 ( java.Long)
语言包 java,Long提供了 java语言的基础类 。 在前面章节已经介绍过的 Object类和 Math类,下面主要介绍数据类型包裹类,字符集类,系统和运行时类等
Java最新实用教程
2009年 7月 27日星期一 48
1) 数据类型包裹类 ( The Data Type Wapper)
对应与 java的每一个基本的数据类型都有一个数据包裹类,如表 5-1所示 。 表 5-1 基本数据类型及数据包裹类基本数据类型 数据包裹类 基本数据类型 数据包裹类
boolean Boolean int Integer
byte Byte long Long
char Character float Float
short Short double Double
每一个包裹类都有一个以相应的数据类型数据为参数的构造方法,可以利用此构造方法生成包裹类的对象 。 如:
double x=12.0
Double a = new Double(x); //生成了一个 Double类的对象 a
Double b= new Double(-1.0); //生成了一个 Double类的对象 b
Java最新实用教程
2009年 7月 27日星期一 49
除了 Character类外,其他包裹类的构造方法的参数还可以是字符串,但必须保证这时的字符串参数能够转换为相应的数据类型。如:
Double c= new Double(“-1.0”); //生成了一个 Double类的对象 c
所有的包裹类对象都有一个静态方法 valueOf() 进行数字数据和字符串数据之间的类型的转换,但对于 Integer类等来说,当需要把一个字符串型的数据转换成数字时,跟常用的是如下方法:
Integer,parseInt(,234”); //返回 int类型的数据 234
Double,ParseDouble(,234”); //返回 double类型的数据
234.000…
2)字符集类( String)
字符集类即为字符串类。 Java将字符串作为类来实现,而不是强制使用字符数组。字符串有两个类,String类和
Java最新实用教程
2009年 7月 27日星期一 50
StringBuffer类。 String 类的字符串对象的值和长度都不变化
,称为常量字符串; StringBuffer类的字符串对象的值和长度都可以变化,称为变量字符串。 String类的应用情况如下:
字符串常量,形如,中国,,,123”,,abc”。
字符串的声明,例如 String s。
创建字符串,例如把上面声明的 str进行实例化,可以用 String类的构造方法:
s = new String(,hello China”);
也可以写成,s =,hello China”;
还可以用一个已创建的字符串创建给另一个字符串,例如用 s
字符串在创建字符串 s1:
String s1 = new String( s);
String类还有以下常用的重载的构造方法,用来将字符数组创建字符串对象 。
Java最新实用教程
2009年 7月 27日星期一 51
String( char a[ ]) //用一个字符数组 a创建一个字符串对象,例如:
char a[ ] =={?h?,?e?,?l?,?l?,?o?};
String s = new String(a);
上述过程相当于:
String s =,hello”;
String( char a[ ],int startIndex,int count) //抽取字符数组 a中的一部分字符创建一个字符串对象,参数 startIndex和 count 分别指定在数组 a中提取字符的起始位置和从该位置开始截取的字符个数,例如:
char a[ ] =={?s?,?t?,?b?,?u?,?s?,?n?};
String s = new String(a,2,3);
上述过程相当于:
String s =,bus”;
获取字符串长度的方法,public int length( ) 。 例如:
Java最新实用教程
2009年 7月 27日星期一 52
String s =,we are friends”,s1=“it is a cat”;
int n1,n2;
n1 = s.length();
n2 = s1.length();
那么 n1的值为 14,n2的值为 11。
字符串的检索
public int indexOf( String s); //字符串调用该方法从当前字符串的头开始检索字符串 s,并返回首次出现 s位置。如果没有检索到字符串 s,则返回值为 -1。
public int indexOf( String s,int standpoint); //字符串调用该方法从当前字符串的 standpoint位置开始检索字符串 s,并返回首次出现 s位置。如果没有检索到字符串 s,则返回值为 -1。
public int lastIndexOf( String s); //字符串调用该方法从当前字符串的
Java最新实用教程
2009年 7月 27日星期一 53
头开始检索字符串 s,并返回最后出现 s位置。如果没有检索到字符串 s,则返回值为 -1。
public int lastindexOf( String s,int standpoint); //字符串调用该方法从当前字符串的 standpoint位置开始检索字符串 s,并返回最后出现 s位置。如果没有检索到字符串 s,则返回值为 -1。
例如:
String s =,i am cute cat”;
s,indexOf(,a”); //值为 2
s,lastIndexOf(,a”); //值为 11
s,indexOf(,t”,1); //值为 7
s,lastIndexOf(,t”,1); //值为 12
字符串的截取
public String subString( int startpoint); //字符串调用该方法获得一个当前字符串的子串,该子串从当前字符串的
startpoint处截取到最后所得到的字符串。
Java最新实用教程
2009年 7月 27日星期一 54
public String subString( int start,int end); //字符串调用该方法获得一个当前字符串的子串,该子串从当前字符串的 start处截取到 end处所得到的字符串,但不包括 end处所对应的字符串

例如:
String s =,dog and cat”;
String s1 = s,subString( 4,7);
则 s1 =,and”。
字符串的替换
public String replace( char oldChar,char newChar); 字符串调用该方法可以获得一个串对象,这个串对象是用 newChar指定的字符替换当前字符串中由 oldChar指定的所有字符而得到的字符串。
public String replaceAll( String old,String new); 字符串
Java最新实用教程
2009年 7月 27日星期一 55
调用该方法可以获得一个串对象,这个串对象是用参数 new指定的字符串替换当前字符串中由 old指定的所有字符串而得到的字符串。
public String trim(); 字符串调用该方法的也可以获得一个字符串对象,该字符串是当前字符串去掉前后空格后的字符串。
例如:
String s =,dog and god,;
String s1 = s,replaceAll(,d”,“g”);
String s2 = s1.trim();
则 s1 =,god and dog,,s2 =,god and dog”;
其他数据类型转化为字符串
public static String valueOf( 数据类型 x); //该字符串的类方法可以将除字符串之外的 任意一种数据类型的数据转换为字符串类型:
Java最新实用教程
2009年 7月 27日星期一 56
public static String valueOf(boolean b); //返回
boolean 参数的字符串表示。如果参数是 true,返回一个等于
,true”的字符串;否则返回一个等于,false”的字符串。
public static String valueOf(int i); //返回
int 参数的字符串表示。
public static String valueOf(long l) //返回代表
long 参数的字符串。
public static String valueOf(float f); //返回
float 参数的字符串表示。
public static String valueOf(double d); //返回
double 参数的字符串表示。
public static String valueOf(Object obj); //如果参数是
null,则返回一个等于,null“的字符串;否则返回对象的字符串表示。
Java最新实用教程
2009年 7月 27日星期一 57
实际上,每种数据包裹类中都有一个将相应类型的数据表示为字符串的方法。
Public static 数据类型 toString();
例如:
String s = String,ValueOf(“123”);
String s1 = Integer,toString(123);
则 s和 s1的内容都是字符串,123” 。
字符串转化为相应的数值如每种数据包裹类中都有一个将相应类型的数据表示为字符串的方法一样,各数据包裹类也有相似的方法将字符串转换成相应的数据类型,且这些方法均为类方法。
例如 Integer类的静态方法 public static int parseInt(
String s) 可将参数 s转换成数据。当然,若 s中含有非数字字符是不能转换的,如 s =,1a”,此时若进行转换则会产生异常
。类似地,java,Long包中的 Byte,Short,Long,Float、
Java最新实用教程
2009年 7月 27日星期一 58
Double类可调用相应的方法:
public static byte parseByte(String s); //返回参数 s对应的 byte
型数值
public static long parseLong(String s); //返回参数 s对应的 long
型数值
public static short parseShort(String s); //返回参数 s对应的 short
型数值
public static float parseFloat(String s); //返回参数 s对应的 float型数值
public static double parseDouble(String s); //返回参数 s对应的
double型数值同时,上面方法的参数中若包含非数字型字符,都会产生异常

例 5.9:将从键盘输入的双精度数据求平均值并显示。
Java最新实用教程
2009年 7月 27日星期一 59
class String_1
{
public static void main(String args[])
{
double sum=0;
int n=args.length;
for(int i=0;i<n;i++)
{
double temp=Double.parseDouble(args[i]);
sum=sum+temp;
}
System.out.println(sum/n);
}
}
运行过程及结果,
>javac String_1.java
>java String_1
14 15 16 17 18
16.0
Java最新实用教程
2009年 7月 27日星期一 60
字符串与字符数组之间的转换
public String char( char a[]); //返回数组 a所有元素按顺序组成的字符串。
public String char( char a[],int sart,int length); //返回数组 a
中从 start开始的 length个元素按顺序组成的字符串。
public char[] toCharArray(); // 把该字符串转换成一个新的字符数组 。
返回一个新分配的字符数组,其长度就是该字符串的长度,内容初始化为该字符串表示的字符序列 。
public void getchars( int start,int end,char a[],int index); //
返回字符串中从位置 start开始到 end-1上的字符复制到数组 a中
,且从 a中 index处开始存放。注意必须保证 a能容纳下要被复制的字符。
3) 系统类 ( System)
Java最新实用教程
2009年 7月 27日星期一 61
System类提供访问系统资源和标准输入输出流的方法。其中访问系统资源的方法:
arrayCopy() //复制数组
exit() //结束当前运行的程序
currentTimeMillis() //获得系统当前日期和时间

其中标准输入输出流包括 System.in和 System.out,并别表示键盘和显示器。
2,实用包( java.util)
实用包提供了实现各种不同功能的类,如日期类( Date) 等。
1) Date类
Date类的构造方法,Date(),获得系统当前日期时间值。如:
System.out.println( new Date());
其结果显示当前日期及时间:
Java最新实用教程
2009年 7月 27日星期一 62
在 Date类中生成某一个特定日期的对象并不容易 。 在 JDK1.1之后的版本中,java放弃了以 Date( int year,int month,int day)
构造的日期,同时 Date类中的许多方法被 Calendar类中的方法取代 。
2) Calendar
Calendar是一个抽象的基础类,支持将 Date对象转换成一系列单个的日期整型数据集,如 YEAR,MONTH,DAY,HOUR等常量 。 由于 Calendar是抽象类,不能用 new方法生成 Calendar的实例对象 。 可以使用 getInstance( ) 方法创建标准日历对象 。
Calendar对象将日历翻到任一个时间的方法:
public final void set( int year,int month,int date)
public final void set( int year,int month,int date,int hour,int
minute)
Java最新实用教程
2009年 7月 27日星期一 63
public final void set( int year,int month,int date,int hour,int
minute,int second)
这三个方法中若参数 year去负数则代表公元前。
Calendar对象获取设定好日期后相关信息的方法。
public int get( int field)
由参数 field指定获取有关当前日历时间的年份、月份、小时、
星期等信息。 Field常取值如下:
Calendar.YEAR 年份
Calendar.MONTH 月份
Calendar.DAY_OF_MONTH 每月中的第几天,每月 1号时此属性值为 1
Calendar.DAY_OF_WEEK 星期,每周日时此属性值为 1等等
获取给定时间域最大值的方法
public int getActualMaximum(int field)
Java最新实用教程
2009年 7月 27日星期一 64
返回给定时间域中的最大可能值。例如当前日历对象的日期是
1997年 2月 3号,且此时的参数 field去值为
Calendar.DAY_OF_MONTH,此时方法的返回值是 29,因为
1997年 3月共有 29天。
例 5.10:编写日历,能显示 2006年 5月的日历页。
import java.util.*;
class Calendar0605
{
public static void main(String args[])
{
Calendar calendar=Calendar.getInstance();
calendar.set(2006,4,1);
int days=calendar.getActualMaximum(Calendar.DAY_OF_MONTH);
System.out.println(2006+“年” +5+“月日历” );
System.out.println(“日 一 二 三 四 五 六” );
int w=calendar.get(Calendar.DAY_OF_WEEK)-1;
String a[]=new String[w+days]; //构造数组
for(int i=0;i<w;i++)
{a[i]="**";}
int n=1;
for(int i=w;i<w+days;i++)
{
Java最新实用教程
2009年 7月 27日星期一 65
if(n<10)
a[i]=" "+(i-w+1);
else
a[i]=String.valueOf(i-w+1);
n++;
}
for(int i=0;i<a.length;i++) //按行打印数组元素
{
if(i%7==0)
System.out.println("\n");
System.out.print(" "+a[i]);
}
}
}
运行结果如图 5-8。
图 5-8 日历对象
Java最新实用教程
2009年 7月 27日星期一 66
3) 分析字符串类 ( StringTokenizer)
有时需要分析字符串并将其分解为可已被独立使用的单词。例如,对于字符串,I am Chinese”,如果把空格作为该串的分隔符,那么该字符串就有三个单词。对字符串进行这类操作时,
需要使用 java.util包中的 StringTokenizer类。
u类有两个常用的构造方法:
StringTokenizer( String s) 该方法为字符串 s构造了一个分析器,使用默认的分隔符,即空格符,换行符,回车符,tab
符,进纸符等 。
StringTokenizer( String s,String delim) 该方法为字符串 s构造了一个分析器,使用字符串 delim指定的分隔符 。
把一个 StringTokenizer对象称作一个字符串分析器。它的常用方法如下:
public int counterTokens()
返回字符串的一共有多少个语言符号(单词)
Java最新实用教程
2009年 7月 27日星期一 67
public String nextToken()
逐个获取以分析的字符串中的语言符号
public Boolean hasMoreToken()
在逐个获取语言符号时判断是否仍有语言符号,有则返回 true
例 5.11:分析字符串,分别输出字符串的单词,并统计单词个数。
import java.util.*;
public class String_Token
{
public static void main(String args[])
{
String s="we are \n good \t firends";
StringTokenizer analyzer=new StringTokenizer(s);
//此句的应用较为关键
int i=analyzer.countTokens();
System.out.println(s);
System.out.println("串中共有 "+i+"个字符 ");
//while语句块用来提取分析的结果
while(analyzer.hasMoreTokens())
{
String temp=analyzer.nextToken();
Java最新实用教程
2009年 7月 27日星期一 68
System.out.println(temp);
}
}
}
图 5-9 字符串分析器表 5-2 格式字符串
y或 yy 表示用两位数输出年份;若要四位数输出年份则用 yyyy指出
M或 MM 表示用两位数输出月份,若要用汉字输出月份则三个以上 M指出,如 MMM
d或 dd 表示用两位数输出日
H或 HH 表示用两位数输出小时
m或 mm 表示用两位数输出分
s或 ss 表示用两位数输出秒
E 表示用字符串输出星期
Java最新实用教程
2009年 7月 27日星期一 69
例如,当想把 2006年 6月 1日用,06年 06月 01日,格式输出显示时,须先创建日期格式化字符串 s=“yy年 MM月 dd日,,然后以 s创建相应的 SimpleDateFormat对象 。
SimpleDateFormat创建了日期显示一个的规格之后,还要把此规格应用于某个给定的日期 。 SimpleDateFormat的方法 format
( Date date) 可将已经设好的的规格套用给参数 date指定的日期或时间 。 例如把当前时间 2006年 6月 28日用,06年 06月 28日
” 格式输出显示的语句为:
String s =,yy年 MM月 dd日,;
SimpleDateFormat frmt = new SimpleDateFormat( s) ;
Date date = new Date( ) ;
System.out.println( frmt.format( date)) ;
5.6.2自定义包上一节介绍了一些预定义好的包,而在实际应用中,用户可以将自己的类组织承包的结构 。
Java最新实用教程
2009年 7月 27日星期一 70
1,包的声明包名必须是合法的标识符,通常包名全部用小写字母,且每个包名必须不同,以防止名称上的冲突 。
包的声明使用 package语句,指明该文件中声明的所有类是属于哪一个包 。 如:
package myweb;
此声明语句说明当前文件中声明的所有类都属于包 myweb。 此文件中的每个类名前都有前缀 myweb,即实际类名应该是
myweb.类名 。 因此,不同包中的类可以同名而不会互相冲突,
因为这些不同包中的类名即使相同,却有不同的前缀 。
程序中如果有 package语句,则 package语句作为源程序的第一条语句,它的前面只能有注释或空行 。 另外,一个文件中最多只能有一条 package语句 。
如果在源文件中没有 package语句,则文件中声明的所有类属于一个默认的无名包 。
Java最新实用教程
2009年 7月 27日星期一 71
在默认的无名包中的类不需要写包前缀。
包声明的完整格式如下:
package pkgname1[.pkgname2[.pkgname3…]] ;
java编译器会自动把包对应于文件系统的目录结构 。 例如前面
myweb包中,所有类文件都存于目录 myweb下,同时,完整定义格式中的,,” 号指明目录的层次 。 例如:
package java.io;
此语句指明这个文件中的所有类都存储在目录 path\java\io下 。
包层次的根目录 path是由环境变量 classpath来确定的 。 为了避免包名冲突,可以将机构的 internet域名反序,作为包名的前导 。
例如 cn.edu.jsj.cuit。
2,编译和生成包如果在程序 Test.java中已经声明了包 myweb,且将程序包存于
e:\web目录下 。 编译时可采用如下格式:
e:\web>javac myweb\Test.java
Java最新实用教程
2009年 7月 27日星期一 72
需要运行时可用如下格式:
e:\web>java myweb.Test
3,包的使用如果其他人想使用前面定义的 Test.java文件中的类,则需要使用 import语句引入包 myweb。 为了能使用自定义包的同时使用系统预置包,必须更新 classpath的设置,可以在命令行中执行如下命令:
set classpath=c:\j2sdk1.4.0\jre\lib\rt.jar;.;e:\
也可以将上述命令添加到环境变量 classpath中 。
只要路径设置没有问题,那么自定义的包和系统预置包的使用就完全一样方便了 。
例 5.12:关于自定义包和引用的简单示例 。
将类打包,假设打包到 h盘,包名为 myweb.person,则应先建立目录 h:\myweb\person。
Java最新实用教程
2009年 7月 27日星期一 73
package myweb.person;
public class Package_1
{
public void intro()
{
int age=12;
String name="甘罗 ";
System.out.println(name+":"+age);
}
}
在工作目录下建立文件来引用上面的包:
import myweb.person.*;
public class Import_1
{
public static void main(String args[])
{
Package_1 p=new Package_1();
p.intro();
}
}
运行步骤及结果如下:
Java最新实用教程
2009年 7月 27日星期一 74
>javac h:\myweb\person\Package_1.java
>set classpath=c:\j2sdk1.4.0\jre\rt.jar;.;h:\
>javac Import_1.java
>java Import_1
甘罗,12
5.6.3JAR文件
1,jar文件格式
JAR( java archive file) 是 java的一种文档格式,以流行的 ZIP
文件格式为基础,与 ZIP 文件不同的是 JAR 文件的内容中,包含一个特殊的文件 META-INF/MANIFESTS.MF( 此文件在生成 jar文件时自动生成 ) 和部署描述符,用来指示工具如何处理特定的 JAR。
2,JAR 文件功能
JAR 文件格式提供了许多优势和功能,其中很多是传统的压缩格式如 ZIP 或者 TAR 所没有提供的 。 它们包括:
Java最新实用教程
2009年 7月 27日星期一 75
减少下载时间:如果一个 applet捆绑到一个 JAR文件中,
那么浏览器就可以在一个 HTTP事务中下载这个 applet的类文件和相关的资源,而不是对每一个文件打开一个新连接。
压缩,JAR 格式允许您压缩文件以提高存储效率。
传输平台扩展,Java 扩展框架( Java Extensions
Framework) 提供了向 Java核心平台添加功能的方法,这些扩展是用 JAR 文件打包的( Java 3D 和 JavaMail就是由 Sun开发的扩展例子)。
包密封:存储在 JAR文件中的包可以选择进行密封,以增强版本一致性和安全性。密封一个包意味着包中的所有类都必须在同一 JAR 文件中找到。
包版本控制:一个 JAR文件可以包含有关它所包含的文件的数据,如厂商和版本信息。
安全性:可以对 JAR文件内容加上数字化签名 。 这样,
能够识别签名的工具就可以有选择地为您授予软件安全特权,
这是其他文件做不到的,它还可以检测代码是否被篡改过 。
Java最新实用教程
2009年 7月 27日星期一 76
可移植性:处理 JAR文件的机制是 Java平台核心 API的标准部分。
3,META-INF目录大多数 JAR文件包含一个 META-INF目录,它用于存储包和扩展的配置数据,如安全性和版本信息 。 Java2 平台识别并解释
META-INF目录中的下述文件和目录,以便配置应用程序,扩展和类装载器:
MANIFEST.MF,这个 manifest文件定义了与扩展和包相关的数据 。
INDEX.LIST,这个文件由 jar工具的新选项 -i 生成,它包含在应用程序或者扩展中定义的包的位置信息 。 它是
JarIndex实现的一部分,并由类装载器用于加速类装载过程 。
xxx.SF,这是 JAR文件的签名文件 。 占位符 xxx 标识了签名者 。
Java最新实用教程
2009年 7月 27日星期一 77
xxx.DSA,与签名文件相关联的签名程序块文件,它存储了用于签名 JAR文件的公共签名 。
4,jar 工具
jar文件是随 jdk安装的,在 jdk安装的目录下的 bin目录中,
Windows下文件夹名为 jar.exe,Linux下文件名为 jar。 它的运行需要用到 jdk安装目录下 lib目录中的 tools.jar文件 。 不过除了安装 jdk什么也不需要做,因为 sun已经帮做好了 。 甚至不需要将
tools.jar放到 classpath中 。
为了用 JAR 文件执行基本的任务,要 使 用 作 为 Java
Development Kit 的一部分提供的 Java Archive Tool (jar 工具 )。
如表 5-3所示用 jar命令调用 jar工具 。
5,可执行的 JAR文件包一个可执行的 jar文件是一个自包含的 Java应用程序,它存储在特别配置的 JAR文件中,可以由 JVM直接执行它而无需事先提取文件或者设置类路径 。
Java最新实用教程
2009年 7月 27日星期一 78
jar cf jar-file input-file..,用一个单独的文件创建一个 JAR文件
jar cf jar-file dir-name 用一个目录创建一个 JAR文件
jar cf0 jar-file dir-name 创建一个未压缩的 JAR文件
jar uf jar-file input-file..,更新一个 JAR文件
jar tf jar-file 查看一个 JAR文件的内容
jar xf jar-file 提取一个 JAR文件的内容
jar xf jar-file archived-file..,从一个 JAR 文件中提取特定的文件
java -jar app.jar 运行一个打包为可执行 JAR 文件的应用程序表 5-3 常见的 jar 工具用法功能命令要运行存储在非可执行的 JAR 中的应用程序,必须将它加入到您的类路径中,并用名字调用应用程序的主类 。 但是使用可执行的 JAR文件,可以不用提取它或者知道主要入口点就可以运行一个应用程序 。 可执行 JAR有助于方便发布和执行 Java 应用程序 。
创建一个可执行 JAR很容易 。 首先将所有应用程序代码放到
Java最新实用教程
2009年 7月 27日星期一 79
一个目录中。假设应用程序中的主类是
com.mycompany.myapp.Sample。
要创建一个包含应用程序代码的 JAR文件并标识出主类 。 为此,
在某个位置 ( 不是在应用程序目录中 ) 创建一个名为 manifest的文件,并在其中加入以下一行:
Main-Class,com.mycompany.myapp.Sample
然后,像这样创建 JAR文件:
jar cmf manifest ExecutableJar.jar application-dir
一个可执行的 JAR必须通过 menifest文件的头引用它所需要的所有 其他 从属 JAR。 如 果 使用 了 - jar 选项,那么 环境 变量
CLASSPATH和在命令行中指定的所有类路径都被 JVM所忽略 。
既然已经将自己的应用程序打包到了一个名为 ExecutableJar.jar
的可执行 JAR了,那么就可以用下面的命令直接从文件启动这个应用程序:
Java最新实用教程
2009年 7月 27日星期一 80
ava -jar ExecutableJar.jar
总之,JAR 格式远远超出了一种压缩格式,它有许多可以改进效率,安全性和组织 Java 应用程序的功能 。 因为这些功能已经建立在核心平台 ( 包括编译器和类装载器 ) 中了,所以开发人员可以利用 JAR文件格式的能力简化和改进开发和部署过程 。
Java最新实用教程
2009年 7月 27日星期一 81
5.7 本章小结本章介绍了 java语言的重用机制,重用的形式可以是类的继承或组合 。 继承关系反映的是类之间一般与特殊的关系,子类继承父类,意味着子类获得了父类的全部属性和行为,然后在此基础上进行扩展或修改 。 本章也介绍了和继承有关的问题:访问权限,隐藏和覆盖,Object类,终结类,抽象类及包的应用