第六章高级语言特征.docx
《第六章高级语言特征.docx》由会员分享,可在线阅读,更多相关《第六章高级语言特征.docx(37页珍藏版)》请在冰点文库上搜索。
第六章高级语言特征
第六章高级语言特征
本模块讨论Java编程语言更多的面向对象特征。
第一节相关问题
讨论-下述问题与本模块中出现的材料相关:
-如何保持一个类或方法不被分成子类或被覆盖?
-如何将数组概念的使用扩展到对象?
第二节目的
完成本模块的学习后,应该能
-描述static变量,方法和初始程序
-描述final类,方法和变量
-列出访问控制级别
-确认降级类并解释如何从JDK1.0升迁到JDK1.1到JDK1.2
-描述如何应用收集和反射
-在Java软件程序中,确认
-static方法和变量
-public,private,protected和缺省变量
-使用abstract类和方法
-解释如何以及何时使用内部类
-解释如何以及何时使用接口
-描述==和equals()之间的不同
第三节类(static)变量
类(static)变量
在所有类的实例中共享
可以被标记为public或private
如果被标记为public而没有该类的实例,可以从该类的外部访问
publicclassCount{
privateintserialNumber;
privatestaticintcounter=0;
publicCount(){
counter++;
serialNumber=counter;
}
}
有时想有一个可以在类的所有实例中共享的变量。
比如,这可以用作实例之间交流的基础或追踪已经创建的实例的数量。
可以用关键字static来标记变量的办法获得这个效果。
这样的变量有时被叫做classvariable,以便与不共享的成员或实例变量区分开来。
publicclassCount{
privateintserialNumber;
privatestaticintcounter=0;
publicCount(){
counter++;
serialNumber=counter;
}
}
在这个例子中,被创建的每个对象被赋于一个独特的序号,从1开始并继续往上。
变量counter在所有实例中共享,所以,当一个对象的构造函数增加counter时,被创建的下一个对象接受增加过的值。
Static变量在某种程度上与其它语言中的全局变量相似。
Java编程语言没有这样的全局语言,但static变量是可以从类的任何实例访问的单个变量。
如果static变量没有被标记成private,它可能会被从该类的外部进行访问。
要这样做,不需要类的实例,可以通过类名指向它。
publicclassStaticVar{
publicstaticintnumber;
}
publicclassOtherClass[
publicvoidmethod(){
intx=StaticVar.number;
}
}
第四节类(static)方法
类(static)方法
没有它所属的类的任何实例,static方法可以被调用
publicclassGeneralFunction{
publicstaticintaddUp(intx,inty){
returnx+y;
}
}
publicclassUseGeneral{
publicvoidmethod(){
inta=9;
intb=10;
intc=GeneralFunction.addUp(a,b);
System.out.println("addUp()gives"+c);
}
}
当没有一个特殊对象变量的实例的时候,有时需要访问程序代码。
用关键字static标记的方法可以这样使用,有时被称做classmethod。
static方法可以用类名而不是引用来访问,如:
publicclassGeneralFunction{
publicstaticintaddUp(intx,inty){
returnx+y;
}
}
publicclassUseGeneral{
publicvoidmethod(){
inta=9;
intb=10;
intc=GeneralFunction.addUp(a,b);
System.out.println("addUp()gives"+c);
}
}
因为static方法不需它所属的类的任何实例就会被调用,因此没有this值。
结果是,static方法不能访问与它本身的参数以及static变量分离的任何变量。
访问非静态变量的尝试会引起编译错误。
注-非静态变量只限于实例,并只能通过实例引用被访问。
publicclassWrong{
intx;
publicstaticvoidmain(Stringargs[]){
x=9;//COMPILERERROR!
}
}
Importpointstorememberaboutstaticmethods:
Main()是静态的,因为它必须在任何实例化发生前被顺序地访问,以便应用程序的运行。
静态方法不能被覆盖成非静态。
第五节静态初始化程序
静态初始化程序
在staticblock中,类可以包含方法程序中不存在的代码。
当类被装载时,静态代码块只执行一次。
方法程序体中不存在的代码在staticblock中类可以包含该代码,这是完全有效的。
当类被装载时,静态块代码只执行一次。
类中不同的静态块按它们在类中出现的顺序被执行。
publicclassStaticInitDemo{
staticinti=5;
static{
System.out.println("Staticcodei="+i++);
}
}
publicclassTest{
publicstaticvoidmain(Stringargs[]){
System.out.println("Maincode:
i="
+StaticInitDemo.i);
}
}
将打印出:
Staticcode:
i=5
Maincode:
i=6
Static方法和数据
第六节一个完整的例子
1.classMyClass{
2.staticintstatInt=4;
3.staticDoublestatDouble=16.0;
4.intinstInt;
5.doubleinstDouble;
6.
7.publicstaticvoidstatMethod(){
8.System.out.println("statInt="+statInt+
9.";statdouble="+statDouble);
10.}
11.publicstaticvoidinstMethod(){
12.System.out.println("instInt="+instInt+
13.";instdouble="+instDouble);
14.}
15.publicMyClass(intintArg,doubledoubleArg){
16.instInt=intArg;
17.instDouble=doubleArg;
18.}
19.publicstaticvoidmain(stringargs[]){
20.MyClassinstance1=newMyClass(1,2.0);
21.MyClassinstance2=newMyClass(3,4.0);
22.
23.MyClass.statMethod();//Outputs:
statInt=4;
24.//statDouble=16.0
25.
26.instance1.instMethod();//Outputs:
instInt=1;
27.//instDouble=2.0
28.instance1.statMethod();//Outputs:
statInt=4;
29.//statDouble=16.0
30.
31.instance2.instMethod();//Outputs:
instInt=3;
32.//instDouble=4.0
33.instance2.statMethod();//Outputs:
statInt=4;
34.//statDouble=16.0
35.}
36.}
37.
图6-1是MyClass类定义的框图。
这个例子阐述了:
1.Static方法和数据的单个(共享)副本是因为类和该类的所有实例而存在。
通过一个实例或通过类本身可以访问static成员。
2.非静态数据只限于实例,只能通过该实例的非静态方法对它进行访问。
非静态数据定义对象之间互不相同的特点,非静态方法在它们所作用的非静态数据的基础上对每个对象的行为互不相同。
考虑一下模仿汽车的特殊类型的一个对象的实例。
轮子的大小,对该类型的所有汽车来说是个常量,可能被模仿成一个静态变量。
颜色根据对象的不同而不同,其行为也根据对象的不同而不同,在它所作用的非静态数据的基础上对不同对象返回不同的颜色。
图6-1Myclass例子
第七节关键字final
6.7.1Final类
关键字final
Final类不能被分成子类
Final方法不能被覆盖
Final变量是常数
Java编程语言允许关键字Final被应用到类中。
如果这样做了,类便不能被子分成子类。
比如,类Java.lang.String就是一个final类。
这样做是出于安全原因,因为它保证,如果方法有字符串的引用,它肯定就是类String的字符串,而不是某个其它类的字符串,这个类是String的被修改过的子类,因为String可能被恶意窜改过。
6.7.2Final方法
个体方法也可以被标记为final。
被标记为final的方法不能被覆盖。
这是由于安全原因。
如果方法具有不能被改变的实现,而且对于对象的一致状态是关键的,那么就要使方法成为final。
被声明为final的方法有时被用于优化。
编译器能产生直接对方法调用的代码,而不是通常的涉及运行时查找的虚拟方法调用。
被标记为static或private的方法被自动地final,因为动态联编在上述两种情况下都不能应用。
6.7.3Final变量
如果变量被标记为final,其结果是使它成为常数。
想改变final变量的值会导致一个编译错误。
下面是一个正确定义final变量的例子:
publicfinalintMAX_ARRAY_SIZE=25;
注-如果将引用类型(即,任何类的类型)的变量标记为final,那么该变量不能指向任何其它对象。
但可能改变对象的内容,因为只有引用本身是final。
第八节抽象类
抽象类
声明方法的存在而不去实现它的类被叫做抽象类
可以通过关键字abstract进行标记将类声明为抽象
publicabstractclassDrawing{
publicabstractvoiddrawDot(intx,inty);
publicvoiddrawLine(intx1,inty1,
intx2,inty2){
//drawusingthedrawDot()methodrepeatedly.
}
}
一个abstract类可以包含非抽象方法和变量
有时在库开发中,要创建一个体现某些基本行为的类,并为该类声明方法,但不能在该类中实现该行为。
取而代之,在子类中实现该方法。
知道其行为的其它类可以在类中实现这些方法。
例如,考虑一个Drawing类。
该类包含用于各种绘图设备的方法,但这些必须以独立平台的方法实现。
它不可能去访问机器的录像硬件而且还必须是独立于平台的。
其意图是绘图类定义哪种方法应该存在,但实际上,由特殊的从属于平台子类去实现这个行为。
正如Drawing类这样的类,它声明方法的存在而不是实现,以及带有对已知行为的方法的实现,这样的类通常被称做抽象类。
通过用关键字abstract进行标记声明一个抽象类。
被声明但没有实现的方法(即,这些没有程序体或{}),也必须标记为抽象。
publicabstractclassDrawing{
publicabstractvoiddrawDot(intx,inty);
publicvoiddrawLine(intx1,inty1,
intx2,inty2){
//drawusingthedrawDot()methodrepeatedly.
}
}
不能创建abstract类的实例。
然而可以创建一个变量,其类型是一个抽象类,并让它指向具体子类的一个实例。
不能有抽象构造函数或抽象静态方法。
Abstract类的子类为它们父类中的所有抽象方法提供实现,否则它们也是抽象类。
publicclassMachineDrawingextendsDrawing{
publicvoiddrawDot(intmachx,intmachy){
//Drawthedot
}
}
Drawingd=newMachineDrawing();
第九节接口
接口
接口是抽象类的变体。
在接口中,所有方法都是抽象的。
多继承性可通过实现这样的接口而获得。
句法是:
publicinterfaceTransparency{
publicstaticfinalintOPAQUE=1;
publicstaticfinalintBITMASK=2;
publicstaticfinalintTRANSLUCENT=3;
publicintgetTransparency();
}
接口是抽象类的变体。
接口中的所有方法都是抽象的,没有一个有程序体。
接口只可以定义staticfinal成员变量。
接口的好处是,它给出了屈从于Java技术单继承规则的假象。
当类定义只能扩展出单个类时,它能实现所需的多个接口。
接口的实现与子类相似,除了该实现类不能从接口定义中继承行为。
当类实现特殊接口时,它定义(即,将程序体给予)所有这种接口的方法。
然后,它可以在实现了该接口的类的任何对象上调用接口的方法。
由于有抽象类,它允许使用接口名作为引用变量的类型。
通常的动态联编将生效。
引用可以转换到接口类型或从接口类型转换,instanceof运算符可以用来决定某对象的类是否实现了接口。
接口是用关键字interface来定义的,如下所述:
publicinterfaceTransparency{
publicstaticfinalintOPAQUE=1;
publicstaticfinalintBITMASK=2;
publicstaticfinalintTRANSLUCENT=3;
publicintgetTransparency();
}
类能实现许多接口。
由类实现的接口出现在类声明的末尾以逗号分隔的列表中,如下所示:
publicclassMyAppletextendsAppletimplements
Runnable,MouseListener{
"..."
}
下例表示一个简单的接口和实现它的一个类:
interfaceSayHello{
voidprintMessage();
}
classSayHelloImplimplementsSayHello{
voidprintMessage(){
System.out.println("Hello");
}
}
interfaceSayHello强制规定,实现它的所有的类必须有一个称做printMessage的方法,该方法带有一个void返回类型且没有输入参数。
接口
对于下述情况,界面是有用的:
声明方法,期望一个或更多的类来实现该方法
决定一个对象的编程界面,而不揭示类的实际程序体
捕获无关类之间的相似性,而不强迫类关系
描述“似函数”对象,它可以作为参数被传递到在其它对象上调用的方法中
对于下述情况,接口是有用的:
声明方法,期望一个或更多的类来实现该方法。
揭示一个对象的编程接口,而不揭示类的实际程序体。
(当将类的一个包输送到其它开发程序中时它是非常有用的。
)
捕获无关类之间的相似性,而不强迫类关系。
描述“似函数”对象,它可以作为参数被传递到在其它对象上调用的方法中。
它们是“函数指针”(用在C和C++中)用法的一个安全的替代用法。
第十节高级访问控制
高级访问控制
修饰符同类同包子类通用性
公共是是是是
受保护是是是
缺省是是
私有是
变量和方法可以处于四个访问级别的一个中;公共,受保护,缺省或私有。
类可以在公共或缺省级别。
变量、方法或类有缺省访问性,如果它没有显式受保护修饰符作为它的声明的一部分的话。
这种访问性意味着,访问可以来自任何方法,当然这些方法只能在作为目标的同一个包中的成员类当中。
以修饰符protected标记的变量或方法实际上比以缺省访问控制标记的更易访问。
一个protected方法或变量可以从类当中的任何方法进行访问,这个类可以是同一个包中的成员,也可以是从任何子类中的任何方法进行访问。
当它适合于一个类的子类但不是不相关的类时,就可以使用这种受保护访问来访问成员。
表6-1总结访问性标准
表6-1访问性标准
修饰符同类同包子类通用性
公共是是是是
受保护是是是
缺省是是
私有是
受保护访问甚至被提供给子类,该子类驻留在与拥有受保护特征的类的不同包中。
第一十一节降级
降级
降级就是过时的构造函数和方法调用。
过时的方法和构造函数由具有更标准化的命名规则的方法所取代。
当升迁代码时,用-deprecation标志来编译代码:
javac-deprecationMyFile.java
在JDK1.1中,对方法名称的标准化做了重大努力。
因此,在JDK1.2中,大量的类构造函数和方法调用过时。
它们由根据更标准化的命名规则规定的方法名称所取代,总的说来,使程序员的生活简单化。
例如,在JDK1.1版本中的Java.awt.Component类:
改变或获得组件大小的方法是resize()和size()。
改变或获得组件矩形框的方法是reshape()和bounds()。
在JDK1.0版本中的Java.awt.Component,这些方法被降级并被以set和get开头表示该方法的初级运算的方法所代替。
setSize()和getSize()
setBounds()getBounds()
无论什么时候将代码从JDK1.0升迁到JDK1.1或更高版本中,或者即使使用以前用在JDK1.0中的代码,对用-deprecation标志来编译代码都是一个好主意。
c:
\javac-deprecationMyFile.java
-deprecation标志将报告在降级过的类中使用的任何方法。
例如,看一个叫做DateConverter的实用类,它将mm/dd/yy格式的日期转换成星期几:
1.packagemyutilities;
2.importjava.util.*;
3.importjava.text.*;
4.publicfinalclassDateConverter{
5.
6.privatestaticStringday_of_the_week[]={"Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"};
7.
8.publicstaticStringgetDayOfWeek(StringtheDate){
9.intmonth,day,year;
10.
11.StringTokenizerst=
12.newStringTokenizer(theDate,"/");
13.month=Integer.parseInt(st.nextToken());
14.day=Integer.parseInt(st.nextToken());
15.year=Integer.parseInt(st.nextToken());
16.Dated=newDate(year,month,day);
17.
18.return(day_of_the_week[d.getDay()]);
19.}
20.}
当这个代码用-deprecation标志在JDK1.2中被编译时,会得到:
c:
\javac-deprecationDateConverter.java
DateConverter.java:
16:
Note:
Theconstructorjava.util.Date(int,int,int)hasbeendeprecated.
Dated=newDate(year,month,day);
^
DateConverter.java:
18:
Note:
ThemethodintgetDay()inclass
java.util.Datehasbeendeprecated.
return(day_of_the_week[d.getDay()]);
^
Note:
DateConverter.javausesadeprecatedAPI.Pleaseconsultthedocumentationforabetteralternative.3warnings
重写的DateConverter类看起来象这样:
1.packagemyutilities;
2.importjava.util.*;
3.importjava.text.*;
4.publicfinalclassDateConverter{
5.
6.privatestaticStringday_Of_The_Week[]=
{"Sunday","Monday","T