JAVA类和对象的高级特征.docx

上传人:b****3 文档编号:4009692 上传时间:2022-11-27 格式:DOCX 页数:14 大小:27.31KB
下载 相关 举报
JAVA类和对象的高级特征.docx_第1页
第1页 / 共14页
JAVA类和对象的高级特征.docx_第2页
第2页 / 共14页
JAVA类和对象的高级特征.docx_第3页
第3页 / 共14页
JAVA类和对象的高级特征.docx_第4页
第4页 / 共14页
JAVA类和对象的高级特征.docx_第5页
第5页 / 共14页
点击查看更多>>
下载资源
资源描述

JAVA类和对象的高级特征.docx

《JAVA类和对象的高级特征.docx》由会员分享,可在线阅读,更多相关《JAVA类和对象的高级特征.docx(14页珍藏版)》请在冰豆网上搜索。

JAVA类和对象的高级特征.docx

JAVA类和对象的高级特征

第四章JAVA类和对象的高级特征

教案名称:

教案大小:

教案类型:

WORD文档

星级评定:

★★★★☆

教案简介:

  本讲主要讲述了java语言中面向对象的高级特征,包括抽象类、接口和包的特性。

通过本讲的学习,同学们可以使用java语言中较为深入的技术编写面向对象程序。

下载一

第四章JAVA类和对象的高级特征

【课前思考】

  1.什么是抽象类、接口?

它们各自又有哪些特性?

  2.你知道java语言在面向对象编程方面有何独特的特点吗?

【学习目标】

  本讲主要讲述了java语言中面向对象的高级特征,包括抽象类、接口和包的特性。

通过本讲的学习,同学们可以使用java语言中较为深入的技术编写面向对象程序。

【学习指南】

  应深刻理解各知识点的概念,使用上一讲的编程基础知识及面向对象技术,编写各种java类,由浅至深,养成风格良好的编程习惯。

【难重点】

 重点:

  1.仔细体会面向对象编程的思想,熟练理解类和对象的概念,理解面向对象的特性,会编写各种java类,逐渐掌握面向对象编程的方法。

  2.注意java语言中,不允许多重继承,而使用接口的方法。

 难点:

  1.理解方法抽象类和接口,不要混淆了两者的使用。

  2.接口的使用。

【知识点】

4.1抽象类

4.2接口

4.3包

4.4JAVA应用程序编程接口

4.1抽象类

java语言中,用abstract关键字来修饰一个类时,这个类叫做抽象类,用abstract关键字来修饰一个方法时,这个方法叫做抽象方法。

格式如下:

  abstractclassabstractClass{…}//抽象类

  abstractreturnTypeabstractMethod([paramlist])//抽象方法

  抽象类必须被继承,抽象方法必须被重写。

抽象方法只需声明,无需实现;抽象类不能被实例化,抽象类不一定要包含抽象方法。

若类中包含了抽象方法,则该类必须被定义为抽象类。

4.2接口

 接口是抽象类的一种,只包含常量和方法的定义,而没有变量和方法的实现,且其方法都是抽象方法。

它的用处体现在下面几个方面:

  ◇通过接口实现不相关类的相同行为,而无需考虑这些类之间的关系。

  ◇通过接口指明多个类需要实现的方法。

  ◇通过接口了解对象的交互界面,而无需了解对象所对应的类。

  1)接口的定义

  接口的定义包括接口声明和接口体。

  接口声明的格式如下:

  [public]interfaceinterfaceName[extendslistOfSuperInterface]{…}

  extends子句与类声明的extends子句基本相同,不同的是一个接口可有多个父接口,用逗号隔开,而一个类只能有一个父类。

  接口体包括常量定义和方法定义

  常量定义格式为:

typeNAME=value;该常量被实现该接口的多个类共享;具有public,final,static的属性。

  方法体定义格式为:

(具有public和abstract属性)

  returnTypemethodName([paramlist]);

  2)接口的实现

  在类的声明中用implements子句来表示一个类使用某个接口,在类体中可以使用接口中定义的常量,而且必须实现接口中定义的所有方法。

一个类可以实现多个接口,在implements子句中用逗号分开。

  3)接口类型的使用

  接口作为一种引用类型来使用。

任何实现该接口的类的实例都可以存储在该接口类型的变量中,通过这些变量可以访问类所实现的接口中的方法。

4.3包

“进行面向对象的设计时,一项基本的考虑是:

如何将发生变化的东西与保持不变的东西分隔开。

”这一点对于库来说是特别重要的。

那个库的用户(客户程序员)必须能依赖自己使用的那一部分,并知道一旦新版本的库出台,自己不需要改写代码。

而与此相反,库的创建者必须能自由地进行修改与改进,同时保证客户程序员代码不会受到那些变动的影响。

为达到这个目的,需遵守一定的约定或规则。

例如,库程序员在修改库内的一个类时,必须保证不删除已有的方法,因为那样做会造成客户程序员代码出现断点。

然而,相反的情况却是令人痛苦的。

对于一个数据成员,库的创建者怎样才能知道哪些数据成员已受到客户程序员的访问呢?

若方法属于某个类唯一的一部分,而且并不一定由客户程序员直接使用,那么这种痛苦的情况同样是真实的。

如果库的创建者想删除一种旧有的实施方案,并置入新代码,此时又该怎么办呢?

对那些成员进行的任何改动都可能中断客户程序员的代码。

所以库创建者处在一个尴尬的境地,似乎根本动弹不得。

为解决这个问题,Java推出了“访问指示符”的概念,允许库创建者声明哪些东西是客户程序员可以使用的,哪些是不可使用的。

这种访问控制的级别在“最大访问”和“最小访问”的范围之间,分别包括:

public,“友好的”(无关键字),protected以及private。

根据前一段的描述,大家或许已总结出作为一名库设计者,应将所有东西都尽可能保持为“private”(私有),并只展示出那些想让客户程序员使用的方法。

这种思路是完全正确的,尽管它有点儿违背那些用其他语言(特别是C)编程的人的直觉,那些人习惯于在没有任何限制的情况下访问所有东西。

到这一章结束时,大家应该可以深刻体会到Java访问控制的价值。

然而,组件库以及控制谁能访问那个库的组件的概念现在仍不是完整的。

仍存在这样一个问题:

如何将组件绑定到单独一个统一的库单元里。

这是通过Java的package(打包)关键字来实现的,而且访问指示符要受到类在相同的包还是在不同的包里的影响。

所以在本章的开头,大家首先要学习库组件如何置入包里。

这样才能理解访问指示符的完整含义。

4.3.1包:

库单元

我们用import关键字导入一个完整的库时,就会获得“包”(Package)。

例如:

importjava.util.*;

它的作用是导入完整的实用工具(Utility)库,该库属于标准Java开发工具包的一部分。

由于Vector位于java.util里,所以现在要么指定完整名称“java.util.Vector”(可省略import语句),要么简单地指定一个“Vector”(因为import是默认的)。

若想导入单独一个类,可在import语句里指定那个类的名字:

importjava.util.Vector;

现在,我们可以自由地使用Vector。

然而,java.util中的其他任何类仍是不可使用的。

之所以要进行这样的导入,是为了提供一种特殊的机制,以便管理“命名空间”(NameSpace)。

我们所有类成员的名字相互间都会隔离起来。

位于类A内的一个方法f()不会与位于类B内的、拥有相同“签名”(自变量列表)的f()发生冲突。

但类名会不会冲突呢?

假设创建一个stack类,将它安装到已有一个stack类(由其他人编写)的机器上,这时会出现什么情况呢?

对于因特网中的Java应用,这种情况会在用户毫不知晓的时候发生,因为类会在运行一个Java程序的时候自动下载。

正是由于存在名字潜在的冲突,所以特别有必要对Java中的命名空间进行完整的控制,而且需要创建一个完全独一无二的名字,无论因特网存在什么样的限制。

迄今为止,本书的大多数例子都仅存在于单个文件中,而且设计成局部(本地)使用,没有同包名发生冲突(在这种情况下,类名置于“默认包”内)。

这是一种有效的做法,而且考虑到问题的简化,本书剩下的部分也将尽可能地采用它。

然而,若计划创建一个“对因特网友好”或者说“适合在因特网使用”的程序,必须考虑如何防止类名的重复。

为Java创建一个源码文件的时候,它通常叫作一个“编辑单元”(有时也叫作“翻译单元”)。

每个编译单元都必须有一个以.java结尾的名字。

而且在编译单元的内部,可以有一个公共(public)类,它必须拥有与文件相同的名字(包括大小写形式,但排除.java文件扩展名)。

如果不这样做,编译器就会报告出错。

每个编译单元内都只能有一个public类(同样地,否则编译器会报告出错)。

那个编译单元剩下的类(如果有的话)可在那个包外面的世界面前隐藏起来,因为它们并非“公共”的(非public),而且它们由用于主public类的“支撑”类组成。

编译一个.java文件时,我们会获得一个名字完全相同的输出文件;但对于.java文件中的每个类,它们都有一个.class扩展名。

因此,我们最终从少量的.java文件里有可能获得数量众多的.class文件。

如以前用一种汇编语言写过程序,那么可能已习惯编译器先分割出一种过渡形式(通常是一个.obj文件),再用一个链接器将其与其他东西封装到一起(生成一个可执行文件),或者与一个库封装到一起(生成一个库)。

但那并不是Java的工作方式。

一个有效的程序就是一系列.class文件,它们可以封装和压缩到一个JAR文件里(使用Java1.1提供的jar工具)。

Java解释器负责对这些文件的寻找、装载和解释(注释①)。

①:

Java并没有强制一定要使用解释器。

一些固有代码的Java编译器可生成单独的可执行文件。

“库”也由一系列类文件构成。

每个文件都有一个public类(并没强迫使用一个public类,但这种情况最很典型的),所以每个文件都有一个组件。

如果想将所有这些组件(它们在各自独立的.java和.class文件里)都归纳到一起,那么package关键字就可以发挥作用)。

若在一个文件的开头使用下述代码:

packagemypackage;

那么package语句必须作为文件的第一个非注释语句出现。

该语句的作用是指出这个编译单元属于名为mypackage的一个库的一部分。

或者换句话说,它表明这个编译单元内的public类名位于mypackage这个名字的下面。

如果其他人想使用这个名字,要么指出完整的名字,要么与mypackage联合使用import关键字(使用前面给出的选项)。

注意根据Java包(封装)的约定,名字内的所有字母都应小写,甚至那些中间单词亦要如此。

例如,假定文件名是MyClass.java。

它意味着在那个文件有一个、而且只能有一个public类。

而且那个类的名字必须是MyClass(包括大小写形式):

packagemypackage;

publicclassMyClass{

//...

现在,如果有人想使用MyClass,或者想使用mypackage内的其他任何public类,他们必须用import关键字激活mypackage内的名字,使它们能够使用。

另一个办法则是指定完整的名称:

mypackage.MyClassm=newmypackage.MyClass();

import关键字则可将其变得简洁得多:

importmypackage.*;

//...

MyClassm=newMyClass();

作为一名库设计者,一定要记住package和import关键字允许我们做的事情就是分割单个全局命名空间,保证我们不会遇到名字的冲突——无论有多少人使用因特网,也无论多少人用Java编写自己的类。

1创建独一无二的包名

大家或许已注意到这样一个事实:

由于一个包永远不会真的“封装”到单独一个文件里面,它可由多个.class文件构成,所以局面可能稍微有些混乱。

为避免这个问题,最合理的一种做法就是将某个特定包使用的所有.class文件都置入单个目录里。

也就是说,我们要利用操作系统的分级文件结构避免出现混乱局面。

这正是Java所采取的方法。

它同时也解决了另两个问题:

创建独一无二的包名以及找出那些可能深藏于目录结构某处的类。

正如我们在第2章讲述的那样,为达到这个目的,需要将.class文件的位置路径编码到package的名字里。

但根据约定,编译器强迫package名的第一部分是类创建者的因特网域名。

由于因特网域名肯定是独一无二的(由InterNIC保证——注释②,它控制着域名的分配),所以假如按这一约定行事,package的名称就肯定不会重复,所以永远不会遇到名称冲突的问题。

换句话说,除非将自己的域名转让给其他人,而且对方也按照相同的路径名编写Java代码,否则名字的冲突是永远不会出现的。

当然,如果你没有自己的域名,那么必须创造一个非常生僻的包名(例如自己的英文姓名),以便尽最大可能创建一个独一无二的包名。

如决定发行自己的Java代码,那么强烈推荐去申请自己的域名,它所需的费用是非常低廉的。

②:

ftp:

//

这个技巧的另一部分是将package名解析成自己机器上的一个目录。

这样一来,Java程序运行并需要装载.class文件的时候(这是动态进行的,在程序需要创建属于那个类的一个对象,或者首次访问那个类的一个static成员时),它就可以找到.class文件驻留的那个目录。

Java解释器的工作程序如下:

首先,它找到环境变量CLASSPATH(将Java或者具有Java解释能力的工具——如浏览器——安装到机器中时,通过操作系统进行设定)。

CLASSPATH包含了一个或多个目录,它们作为一种特殊的“根”使用,从这里展开对.class文件的搜索。

从那个根开始,解释器会寻找包名,并将每个点号(句点)替换成一个斜杠,从而生成从CLASSPATH根开始的一个路径名(所以packagefoo.bar.baz会变成foo\bar\baz或者foo/bar/baz;具体是正斜杠还是反斜杠由操作系统决定)。

随后将它们连接到一起,成为CLASSPATH内的各个条目(入口)。

以后搜索.class文件时,就可从这些地方开始查找与准备创建的类名对应的名字。

此外,它也会搜索一些标准目录——这些目录与Java解释器驻留的地方有关。

为进一步理解这个问题,下面以我自己的域名为例,它是。

将其反转过来后,com.bruceeckel就为我的类创建了独一无二的全局名称(com,edu,org,net等扩展名以前在Java包中都是大写的,但自Java1.2以来,这种情况已发生了变化。

现在整个包名都是小写的)。

由于决定创建一个名为util的库,我可以进一步地分割它,所以最后得到的包名如下:

packagecom.bruceeckel.util;

现在,可将这个包名作为下述两个文件的“命名空间”使用:

//:

Vector.java

//Creatingapackage

packagecom.bruceeckel.util;

publicclassVector{

publicVector(){

System.out.println(

"com.bruceeckel.util.Vector");

}

}///:

~

创建自己的包时,要求package语句必须是文件中的第一个“非注释”代码。

第二个文件表面看起来是类似的:

//:

List.java

//Creatingapackage

packagecom.bruceeckel.util;

publicclassList{

publicList(){

System.out.println(

"com.bruceeckel.util.List");

}

}///:

~

这两个文件都置于我自己系统的一个子目录中:

C:

\DOC\JavaT\com\bruceeckel\util

若通过它往回走,就会发现包名com.bruceeckel.util,但路径的第一部分又是什么呢?

这是由CLASSPATH环境变量决定的。

在我的机器上,它是:

CLASSPATH=.;D:

\JAVA\LIB;C:

\DOC\JavaT

可以看出,CLASSPATH里能包含大量备用的搜索路径。

然而,使用JAR文件时要注意一个问题:

必须将JAR文件的名字置于类路径里,而不仅仅是它所在的路径。

所以对一个名为grape.jar的JAR文件来说,我们的类路径需要包括:

CLASSPATH=.;D:

\JAVA\LIB;C:

\flavors\grape.jar

正确设置好类路径后,可将下面这个文件置于任何目录里:

//:

LibTest.java

//Usesthelibrary

packagec05;

importcom.bruceeckel.util.*;

publicclassLibTest{

publicstaticvoidmain(String[]args){

Vectorv=newVector();

Listl=newList();

}

}///:

~

编译器遇到import语句后,它会搜索由CLASSPATH指定的目录,查找子目录com\bruceeckel\util,然后查找名称适当的已编译文件(对于Vector是Vector.class,对于List则是List.class)。

注意Vector和List内无论类还是需要的方法都必须设为public。

1.自动编译

为导入的类首次创建一个对象时(或者访问一个类的static成员时),编译器会在适当的目录里寻找同名的.class文件(所以如果创建类X的一个对象,就应该是X.class)。

若只发现X.class,它就是必须使用的那一个类。

然而,如果它在相同的目录中还发现了一个X.java,编译器就会比较两个文件的日期标记。

如果X.java比X.class新,就会自动编译X.java,生成一个最新的X.class。

对于一个特定的类,或在与它同名的.java文件中没有找到它,就会对那个类采取上述的处理。

2.冲突

若通过*导入了两个库,而且它们包括相同的名字,这时会出现什么情况呢?

例如,假定一个程序使用了下述导入语句:

importcom.bruceeckel.util.*;

importjava.util.*;

由于java.util.*也包含了一个Vector类,所以这会造成潜在的冲突。

然而,只要冲突并不真的发生,那么就不会产生任何问题——这当然是最理想的情况,因为否则的话,就需要进行大量编程工作,防范那些可能可能永远也不会发生的冲突。

如现在试着生成一个Vector,就肯定会发生冲突。

如下所示:

Vectorv=newVector();

它引用的到底是哪个Vector类呢?

编译器对这个问题没有答案,读者也不可能知道。

所以编译器会报告一个错误,强迫我们进行明确的说明。

例如,假设我想使用标准的JavaVector,那么必须象下面这样编程:

java.util.Vectorv=newjava.util.Vector();

由于它(与CLASSPATH一起)完整指定了那个Vector的位置,所以不再需要importjava.util.*语句,除非还想使用来自java.util的其他东西。

4.3.2自定义工具库

掌握前述的知识后,接下来就可以开始创建自己的工具库,以便减少或者完全消除重复的代码。

例如,可为System.out.println()创建一个别名,减少重复键入的代码量。

它可以是名为tools的一个包(package)的一部分:

//:

P.java

//TheP.rint&P.rintlnshorthand

packagecom.bruceeckel.tools;

publicclassP{

publicstaticvoidrint(Objectobj){

System.out.print(obj);

}

publicstaticvoidrint(Strings){

System.out.print(s);

}

publicstaticvoidrint(char[]s){

System.out.print(s);

}

publicstaticvoidrint(charc){

System.out.print(c);

}

publicstaticvoidrint(inti){

System.out.print(i);

}

publicstaticvoidrint(longl){

System.out.print(l);

}

publicstaticvoidrint(floatf){

System.out.print(f);

}

publicstaticvoidrint(doubled){

System.out.print(d);

}

publicstaticvoidrint(booleanb){

System.out.print(b);

}

publicstaticvoidrintln(){

System.out.println();

}

publicstaticvoidrintln(Objectobj){

System.out.println(obj);

}

publicstaticvoidrintln(Strings){

System.out.println(s);

}

publicstaticvoidrintln(char[]s){

System.out.println(s);

}

publicstaticvoidrintln(charc){

System.out.println(c);

}

publicstaticvoidrintln(inti){

System.out.println(i);

}

publicstaticvoidrintln(longl){

System.out.println(l);

}

publicstaticvoidrintln(floatf){

System.out.println(f);

}

publicstaticvoidrintln(doubled){

System.out.println(d);

}

publicstaticvoidrintln(booleanb){

System.out.println(b);

}

}///:

~

所有不同的数据类型现在都可以在一个新行输出(P.rintln()),或者不在一个新行输出(P.rint())。

大家可能会猜想这个文件所在的目录必须从某个CLASSPATH位置开始,然后继续com/bruceeckel/tools。

编译完毕后,利用一个import语句,即可在自己系统的任何地方使用P.class文件。

如下所示:

ToolTest.java

所以从现在开始,无论什么时候只要做出了一个有用的新工具,就可将其加入tools目录(或者自己的个人util或tools目录)。

1.CLASSPATH的陷阱

P.java文件存在一个非常有趣的陷阱。

特别是对于早期的Java实现方案来说,类路径的正确设定通常都是很困难的一项工作。

编写这本书的时候,我引入了P.java文件,它最初看起来似乎工作很正常。

但在某些情况下,却开始出现中断。

在很长的时间里,我都确信这是Java或其他什么在实现时一个错误。

但最后,我终于发现在一个地方引入了一个程序(即第17章要说明的CodePac

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 高等教育 > 其它

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1