第六章 高级语言特征 本模块讨论Java编程语言更多的面向对象特征。 第一节 相关问题 讨论-下述问题与本模块中出现的材料相关: 如何保持一个类或方法不被分成子类或被覆盖? 如何将数组概念的使用扩展到对象? 第二节 目 的 完成本模块的学习后,应该能 描述static变量,方法和初始程序 描述final类,方法和变量 列出访问控制级别 确认降级类并解释如何从JDK1.0升迁到JDK1.1到JDK1.2 描述如何应用收集和反射 在Java软件程序中,确认 static方法和变量 public,private,protected和缺省变量 使用abstract类和方法 解释如何以及何时使用内部类 解释如何以及何时使用接口 描述==和equals()之间的不同 第三节 类(static)变量 类(static)变量 在所有类的实例中共享 可以被标记为public或private 如果被标记为public而没有该类的实例,可以从该类的外部访问 public class Count { private int serialNumber; private static int counter = 0; public Count() { counter++; serialNumber = counter; } }   有时想有一个可以在类的所有实例中共享的变量。比如,这可以用作实例之间交流的基础或追踪已经创建的实例的数量。 可以用关键字static来标记变量的办法获得这个效果。这样的变量有时被叫做class variable,以便与不共享的成员或实例变量区分开来。 public class Count { private int serialNumber; private static int counter = 0; public Count() { counter++; serialNumber = counter; } } 在这个例子中,被创建的每个对象被赋于一个独特的序号,从1开始并继续往上。变量counter在所有实例中共享,所以,当一个对象的构造函数增加counter时,被创建的下一个对象接受增加过的值。 Static变量在某种程度上与其它语言中的全局变量相似。Java编程语言没有这样的全局语言,但static变量是可以从类的任何实例访问的单个变量。 如果static变量没有被标记成private,它可能会被从该类的外部进行访问。要这样做,不需要类的实例,可以通过类名指向它。 public class StaticVar { public static int number; } public class OtherClass [ public void method() { int x = StaticVar.number; } } 类(static)方法 类(static)方法 没有它所属的类的任何实例,static方法可以被调用 public class GeneralFunction { public static int addUp(int x, int y) { return x + y; } } public class UseGeneral { public void method() { int a = 9; int b = 10; int c = GeneralFunction.addUp(a, b); System.out.println("addUp() gives " + c); } }   当没有一个特殊对象变量的实例的时候,有时需要访问程序代码。用关键字static标记的方法可以这样使用,有时被称做class method。static方法可以用类名而不是引用来访问,如: public class GeneralFunction { public static int addUp(int x, int y) { return x + y; } } public class UseGeneral { public void method() { int a = 9; int b = 10; int c = GeneralFunction.addUp(a, b); System.out.println("addUp() gives " + c); } } 因为static方法不需它所属的类的任何实例就会被调用,因此没有this值。结果是,static方法不能访问与它本身的参数以及static变量分离的任何变量。访问非静态变量的尝试会引起编译错误。 注-非静态变量只限于实例,并只能通过实例引用被访问。   public class Wrong { int x; public static void main(String args[]) { x = 9; // COMPILER ERROR! } } Import points to remember about static methods: Main()是静态的,因为它必须在任何实例化发生前被顺序地访问,以便应用程序的运行。 静态方法不能被覆盖成非静态。 静态初始化程序 静态初始化程序 在static block中,类可以包含方法程序中不存在的代码。 当类被装载时,静态代码块只执行一次。   方法程序体中不存在的代码在static block中类可以包含该代码,这是完全有效的。当类被装载时,静态块代码只执行一次。类中不同的静态块按它们在类中出现的顺序被执行。 public class StaticInitDemo { static int i = 5; static { System.out.println("Static code i= "+ i++ ); } } public class Test { public static void main(String args[]) { System.out.println("Main code: i=" + StaticInitDemo.i); } } 将打印出: Static code: i=5 Main code: i=6 Static 方法和数据 第六节 一个完整的例子 class MyClass { static int statInt = 4; static Double statDouble = 16.0; int instInt; double instDouble; public static void statMethod(){ System.out.println ("statInt="+statInt+ ";statdouble="+statDouble); } public static void instMethod(){ System.out.println("instInt="+instInt+ ";instdouble="+instDouble); } public MyClass(int intArg, double doubleArg){ instInt = intArg; instDouble = doubleArg; } public static void main(string args[]){ MyClass instance1 = new MyClass(1,2.0); MyClass instance2 = new MyClass(3,4.0); MyClass.statMethod(); //Outputs:statInt=4; //statDouble=16.0 instance1.instMethod(); //Outputs:instInt=1; //instDouble=2.0 instance1.statMethod(); //Outputs:statInt=4; //statDouble=16.0 instance2.instMethod(); //Outputs:instInt=3; //instDouble=4.0 instance2.statMethod(); //Outputs:statInt=4; //statDouble=16.0 } } 37. 图6-1是MyClass类定义的框图。这个例子阐述了: Static方法和数据的单个(共享)副本是因为类和该类的所有实例而存在。通过一个实例或通过类本身可以访问static成员。 非静态数据只限于实例,只能通过该实例的非静态方法对它进行访问。非静态数据定义对象之间互不相同的特点,非静态方法在它们所作用的非静态数据的基础上对每个对象的行为互不相同。 考虑一下模仿汽车的特殊类型的一个对象的实例。轮子的大小,对该类型的所有汽车来说是个常量,可能被模仿成一个静态变量。颜色根据对象的不同而不同,其行为也根据对象的不同而不同,在它所作用的非静态数据的基础上对不同对象返回不同的颜色。 图6-1 Myclass 例子 第七节 关键字final 6.7.1 Final类 关键字final Final类不能被分成子类 Final方法不能被覆盖 Final变量是常数   Java编程语言允许关键字Final被应用到类中。如果这样做了,类便不能被子分成子类。比如,类Java.lang.String就是一个final类。这样做是出于安全原因,因为它保证,如果方法有字符串的引用,它肯定就是类String的字符串,而不是某个其它类的字符串,这个类是String的被修改过的子类,因为String可能被恶意窜改过。 6.7.2 Final方法 个体方法也可以被标记为final。被标记为final的方法不能被覆盖。这是由于安全原因。如果方法具有不能被改变的实现,而且对于对象的一致状态是关键的,那么就要使方法成为final。 被声明为final的方法有时被用于优化。编译器能产生直接对方法调用的代码,而不是通常的涉及运行时查找的虚拟方法调用。 被标记为static或private的方法被自动地final,因为动态联编在上述两种情况下都不能应用。 6.7.3 Final变量 如果变量被标记为final,其结果是使它成为常数。想改变final变量的值会导致一个编译错误。下面是一个正确定义final变量的例子: public final int MAX_ARRAY_SIZE = 25; 注-如果将引用类型(即,任何类的类型)的变量标记为final,那么该变量不能指向任何其它对象。但可能改变对象的内容,因为只有引用本身是final。   抽象类 抽象类 声明方法的存在而不去实现它的类被叫做抽象类 可以通过关键字abstract进行标记将类声明为抽象 public abstract class Drawing { public abstract void drawDot(int x, int y); public void drawLine(int x1, int y1, int x2, int y2) { // draw using the drawDot() method repeatedly. } } 一个abstract类可以包含非抽象方法和变量   有时在库开发中,要创建一个体现某些基本行为的类,并为该类声明方法,但不能在该类中实现该行为。取而代之,在子类中实现该方法。知道其行为的其它类可以在类中实现这些方法。 例如,考虑一个Drawing类。该类包含用于各种绘图设备的方法,但这些必须以独立平台的方法实现。它不可能去访问机器的录像硬件而且还必须是独立于平台的。其意图是绘图类定义哪种方法应该存在,但实际上,由特殊的从属于平台子类去实现这个行为。 正如Drawing类这样的类,它声明方法的存在而不是实现,以及带有对已知行为的方法的实现,这样的类通常被称做抽象类。通过用关键字abstract进行标记声明一个抽象类。被声明但没有实现的方法(即,这些没有程序体或{}),也必须标记为抽象。 public abstract class Drawing { public abstract void drawDot(int x, int y); public void drawLine(int x1, int y1, int x2, int y2) { // draw using the drawDot() method repeatedly. } } 不能创建abstract类的实例。然而可以创建一个变量,其类型是一个抽象类,并让它指向具体子类的一个实例。不能有抽象构造函数或抽象静态方法。 Abstract类的子类为它们父类中的所有抽象方法提供实现,否则它们也是抽象类。 public class MachineDrawing extends Drawing { public void drawDot (int mach x, intmach y) { // Draw the dot } } Drawing d = new MachineDrawing(); 第九节 接 口 接 口 接口是抽象类的变体。 在接口中,所有方法都是抽象的。 多继承性可通过实现这样的接口而获得。 句法是: public interface Transparency { public static final int OPAQUE=1; public static final int BITMASK=2; public static final int TRANSLUCENT=3; public int getTransparency(); }   接口是抽象类的变体。接口中的所有方法都是抽象的,没有一个有程序体。接口只可以定义static final成员变量。 接口的好处是,它给出了屈从于Java技术单继承规则的假象。当类定义只能扩展出单个类时,它能实现所需的多个接口。 接口的实现与子类相似,除了该实现类不能从接口定义中继承行为。当类实现特殊接口时,它定义(即,将程序体给予)所有这种接口的方法。然后,它可以在实现了该接口的类的任何对象上调用接口的方法。由于有抽象类,它允许使用接口名作为引用变量的类型。通常的动态联编将生效。引用可以转换到接口类型或从接口类型转换,instanceof运算符可以用来决定某对象的类是否实现了接口。 接口是用关键字interface来定义的,如下所述: public interface Transparency { public static final int OPAQUE=1; public static final int BITMASK=2; public static final int TRANSLUCENT=3; public int getTransparency(); } 类能实现许多接口。由类实现的接口出现在类声明的末尾以逗号分隔的列表中,如下所示: public class MyApplet extends Applet implements Runnable, MouseListener{ "..." } 下例表示一个简单的接口和实现它的一个类: interface SayHello { void printMessage(); } class SayHelloImpl implements SayHello { void printMessage() { System.out.println("Hello"); } } interface SayHello强制规定,实现它的所有的类必须有一个称做printMessage的方法,该方法带有一个void返回类型且没有输入参数。 接 口 对于下述情况,界面是有用的: 声明方法,期望一个或更多的类来实现该方法 决定一个对象的编程界面,而不揭示类的实际程序体 捕获无关类之间的相似性,而不强迫类关系 描述“似函数”对象,它可以作为参数被传递到在其它对象上调用的方法中   对于下述情况,接口是有用的: 声明方法,期望一个或更多的类来实现该方法。 揭示一个对象的编程接口,而不揭示类的实际程序体。(当将类的一个包输送到其它开发程序中时它是非常有用的。) 捕获无关类之间的相似性,而不强迫类关系。 描述“似函数”对象,它可以作为参数被传递到在其它对象上调用的方法中。它们是“函数指针”(用在C和C++中)用法的一个安全的替代用法。 第十节 高级访问控制 高级访问控制 修饰符 同类 同包 子类 通用性 公共 是 是 是 是 受保护 是 是 是 缺省 是 是 私有 是   变量和方法可以处于四个访问级别的一个中;公共,受保护,缺省或私有。类可以在公共或缺省级别。 变量、方法或类有缺省访问性,如果它没有显式受保护修饰符作为它的声明的一部分的话。这种访问性意味着,访问可以来自任何方法,当然这些方法只能在作为目标的同一个包中的成员类当中。 以修饰符protected标记的变量或方法实际上比以缺省访问控制标记的更易访问。一个protected方法或变量可以从类当中的任何方法进行访问,这个类可以是同一个包中的成员,也可以是从任何子类中的任何方法进行访问。当它适合于一个类的子类但不是不相关的类时,就可以使用这种受保护访问来访问成员。 表6-1总结访问性标准 表6-1 访问性标准 修饰符 同类 同包 子类 通用性 公共 是 是 是 是 受保护 是 是 是 缺省 是 是 私有 是 受保护访问甚至被提供给子类,该子类驻留在与拥有受保护特征的类的不同包中。 降级 降 级 降级就是过时的构造函数和方法调用。 过时的方法和构造函数由具有更标准化的命名规则的方法所取代。 当升迁代码时,用-deprecation标志来编译代码: javac -deprecation MyFile.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 -deprecation MyFile.java -deprecation标志将报告在降级过的类中使用的任何方法。例如,看一个叫做DateConverter的实用类,它将mm/dd/yy格式的日期转换成星期几: package myutilities; import java.util.*; import java.text.*; public final class DateConverter { private static String day_of_the_week [] = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"}; public static String getDayOfWeek (String theDate){ int month, day, year; StringTokenizer st = new StringTokenizer (theDate, "/"); month = Integer.parseInt(st.nextToken ()); day = Integer.parseInt(st.nextToken()); year = Integer.parseInt(st.nextToken()); Date d = new Date (year, month, day); return (day_of_the_week[d.getDay()]); } } 当这个代码用-deprecation标志在JDK1.2中被编译时,会得到: c:\ javac -deprecation DateConverter.java DateConverter.java:16: Note: The constructor java.util.Date(int,int,int) has been deprecated. Date d = new Date (year, month, day); ^ DateConverter.java:18: Note: The method int getDay() in class java.util.Date has been deprecated. return (day_of_the_week[d.getDay()]); ^ Note: DateConverter.java uses a deprecated API.Please consult the documentation for a better alternative. 3 warnings 重写的DateConverter类看起来象这样: package myutilities; import java.util.*; import java.text.*; public final class DateConverter { private static String day_Of_The_Week [] = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"}; 7. public static String getDayOfWeek (String theDate){ Date d = null; SimpleDateFormat sdf = new SimpleDateFormat ("MM/dd/yy"); try { d = sdf.parse (theDate); } catch (ParseException e) { System.out.println (e); e.printStackTrace(); } // Create a GregorianCalendar object Calendar c = new GregorianCalendar (TimeZone.getTimeZone ("EST"), Locale.US); c.setTime (d); return(day_Of_The_Week [(c.get(Calendar.DAY_OF_WEEK)-1)]); } } 在这里,1.2版本使用两个新类:SimpleDateFormat,用来采用任何String日期格式并创建一个Date对象的类;以及GregorianCalendar类,用来创建一个带有当地时区和场所的日历。 ==运算符与equals()方法 ==运算符与equals()方法 equals()和==方法决定引用值是否指向同一对象 equals()在类中被覆盖,为的是当两个分离的对象的内容和类型相配的话,返回真值。   Java.lang包中的Object类有public boolean equals (Object obj)方法。它比较两个对象是否相等。仅当被比较的两个引用指向同一对象时,对象的equals()方法返回true。 ==运算符也进行等值比较。也就是说,对于任何引用值X和Y,当且仅当X和Y指向同一对象时, X==Y返回真。 当两个分离的对象的内容和类型相配的话,String,Date,File类和所有其它override equals()的包装类(Integer,Double,等等)将返回真。 例如,String类中的equals()方法返回true,当且仅当参数不为null并且是一个String对象,该对象与调用该方法的String对象具有相同的字符顺序。 String s1=new String(“JDK1.2”); String s2=new String(“JDK1.2”); 方法s1.equals(s2)返回真,尽管s1和s2指向两个不同的对象。 toString()方法 toString()方法 被用来将一个对象转换成String 被用来将一个基本类型转换成String   toString方法被用来将一个对象转换成String表达式。当自动字符串转换发生时,它被用作编译程序的参照。System.out.println()调用下述代码: Date now = new Date() System.out.println(now) 将被翻译成: System.out.println(now.toString()); 对象类定义缺省的toString()方法,它返回类名称和它的引用的地址(通常情况下不是很有用)。许多类覆盖toString()以提供更有用的信息。例如,所有的包装类覆盖toString()以提供它们所代表的值的字符串格式。甚至没有字符串格式的类为了调试目的常常实现toString()来返回对象状态信息。 第十四节 内部类 内部类 被附加到JDK1.1 允许一个类定义被放到另一个类定义里 把类在逻辑上组织在一起 可访问它们所嵌套的类的范围   内部类,有时叫做嵌套类,被附加到JDK1.1及更高版本中。内部类允许一个类定义被放到另一个类定义里。内部类是一个有用的特征,因为它们允许将逻辑上同属性的类组合到一起,并在另一个类中控制一个类的可视性。 6.14.1 内部类基础 下述例子表示使用内部类的共同方法: import java.awt.*; import java.awt.event.*; public class MyFrame extends Frame{ Button myButton; TextArea myTextArea; int count;; public MyFrame(){ super("Inner Class Frame"); myButton = new Button("click me"); myTextArea = new TextArea(); add(myButton,BorderLayout,CENTER); add(myTextArea,BorderLayout,NORTH); ButtonListener bList = new ButtonListener(); myButton.addActionListener(bList); } class ButtonListener implements ActionListener{ public void actionPerformed(ActionEvent e){ count ++ myTextArea.setText("button clicked" + { count + "times"); } }// end of innerclass ButtonListener public static void main(String args[]){ MyFrame f = new MyFrame(); f.setSize(300,300); f.setVisible(true); } } // end of class MyFrame 前面的例子包含一个类MyFrame,它包括一个内部类ButtonListener。编译器生成一个类文件,MyFrame$ButtonListener.class以及toMyFrame.class。它包含在MyFrame.class中,是在类的外部创建的。 6.14.2 如何做内部类工作? 内部类可访问它们所嵌套的类的范围。所嵌套的类的成员的访问性是关键而且很有用。对嵌套类的范围的访问是可能的,因为内部类实际上有一个隐含的引用指向外部类上下文(如外部类“this”)。 public class MyFrame extends Frame{ Button myButton; TextArea myTextarea; public MyFrame(){ ...................... ...................... MyFrame$ButtonListener bList = new MyFrame$ButtonListener(this); myButton.addActionListener(bList); } class MyFrame$ButtonListener implements ActionListener{ private MyFrame outerThis; Myframe$ButtonListener(MyFrame outerThisArg){ outerThis = outerThisArg; } public void actionPerformed(ActionEvent e) { outerThis.MyTextArea.setText("buttonclicked"); ...................... ...................... } public static void main(String args[]){ MyFrame f = new MyFrame(); f.setSize(300,300); f.setVisible(true); } } 有时可能要从static方法或在没有this的某些其它情况下,创建一个内部类的一个实例(例如,main)。可以如下这么做: public static void main(String args[]){ MyFrame f = new MyFrame(); MyFrame.ButtonListener bList = f.new ButtonListener(); f.setSize(50,50); f.setVisible(true); } 6.14.3 内部类属性 内部类属性 类名称只能用在定义过的范围中,除非用限定的名称。 内部类的名称必须与所嵌套的类不同。 内部类可以被定义在方法中。 任何变量,不论是本地变量还是正式参数,如果变量被标记为final,那么,就可以被内部类中的方法访问。   内部类有如下属性: 类名称只能用在定义过的范围中,除非用在限定的名称中。内部类的名称必须与所嵌套的类不同。 内部类可以被定义在方法中。这条规则较简单,它支配到所嵌套类方法的变量的访问。任何变量,不论是本地变量还是正式参数,如果变量被标记为final,那么,就可以被内部类中的方法访问。 内部类可以使用所嵌套类的类和实例变量以及所嵌套的块中的本地变量。 内部类可以被定义为abstract. 属性 只有内部类可以被声明为private或protected,以便防护它们不受来自外部类的访问。访问保护不阻止内部类使用其它类的任何成员,只要一个类嵌套另一个。 一个内部类可以作为一个接口,由另一个内部类实现。 被自动地声明为static的内部类成为顶层类。这些内部类失去了在本地范围和其它内部类中使用数据或变量的能力。 内部类不能声明任何static成员;只有顶层类可以声明static成员。因此,一个需求static成员的内部类必须使用来自顶层类的成员。 注-内部类有常常被用作创建事件适配器的方便特征。事件适配器将在后面模块中讨论。   第十五节 包装类 包装类 用来将基本数据元素看作对象,包装类可以被用作: 基本数据类型 包装类 boolean Boolean byte Byte char Char short Short int Int long Long float Float double Double   Java编程语言不把基本数据类型看作对象。例如,在基本格式本身当中,数字、布尔及字符型数据都被看作是为了提高效率。Java编程语言提供包装类来将基本数据元素看作对象。这样的数据元素被包裹在创建于它们周围的对象中,每个Java基本数据类型在Java.lang包中都有一个相应的wrapper class。每个包装类对象封装一个基本值。 包装类列表如下: 表6-2 包装类 基本数据类型 包装类 boolean Boolean byte Byte char Char short Short int Int long Long float Float double Double 可以通过将被包裹的值传递到合适的构造函数中来构造包装类对象。例如: int pInt = 500; Integer wInt = new Integer(pInt); 第十六节 收集API 收集API 收集(或容器)是代表一个对象组的单个对象,被认为是它的元素。 收集类Vector,Bits,Stack,Hashtable,LinkedList等等都被支持。 收集API包括将对象保持为下述情况的界面: 收集-没有具体顺序的一组对象 设定-没有复制的一组对象 列表-有序对象组,允许复制   收集(或容器)是代表一个对象组的单个对象,被认为是它的元素。收集典型地处理许多对象的类型,所有的类型都有一个特殊的种类(也就是说,它们都是从一个共同父类型延续来的)。Java编程语言支持收集类Vector,Bits, Stack,Hashtable ,LinkedList等等。例如,Stack实现后进先出(LIFO)的顺序,Hashtable提供一个相关的对象数组。 收集保持处理Object类型的对象。这允许在收集中贮存任何对象。它还可以,在使用对象前、从收集中检索到它之后,使用正确的类型转换。 收集API典型地由将对象保持为下述情况的接口而组成: 收集-没有具体顺序的一组对象 设定-没有复制的一组对象 列表-有序对象组,允许复制 API还包括诸如HashSet, ArraySet, ArrayList, LinkedList和Vector等等的类,它们实现这些接口。API还提供了支持某些算法的方法,如:排序,二进制搜索,评估列表中的最小和最大,以及收集等。 第十七节 Vector类 Vector类 Vector类为与各种元素类型的动态数组一起工作提供方法。 Java.lang.Object │ └-------Java.util.AbstractCollection └---Java.util.AbstractList └----Java.util.Vector  Vector类为与各种元素类型的动态数组一起工作提供方法。 6.17.1 提要 提 要 每个矢量保持一个capacity和capacityIncrement。 因为附加了元素,矢量的贮存在信息块上增加到capacityIncrement变量的大小。  public class Vector extends AbstractList implements List, Cloneable, Serlializable 每个矢量保持一个capacity和capacityIncrement。因为附加了元素到矢量,矢量的贮存在信息块上增加到capacityIncrement变量的大小。一个矢量的容量总是至少与矢量的大小一样大(通常情况下更大)。 6.17.2 构造函数 构造函数 public Vector() public Vector(int initialCapacity) public Vector(int initialCapacity,int capacityInctrment)   Vector类的构造函数是 public Vector()-构造一个空矢量 public Vector(int initialCapacity)-构造一个具有具体贮存容量的空矢量 public Vector(int initialCapacity,int capacityInctrment)-构造具有具体贮存容量和具体capacityInctrment的空矢量。 6.17.3 变量 变 量 protected int capacityIncrement protected int elementCount protected Object elementData[]   Vector类包含下述实例变量: protected int capacityIncrement -增加量。(如为0,每次需要增加时,缓冲区的大小成倍。) protected int elementCount -缓冲区中元素的数量。 protected Object elementData[]-元素被贮存的缓冲区。 6.17.4 方法 下面是Vector类中的一些方法。参照Java API,看该类中所有方法的描述。 public final int size()-返回矢量中元素的数量。(这与矢量的容量不一样。) public final boolean contains(Object elem)-如果指定对象是收集的值,返回真。 public final int indexOf (Object elem)-从起始位置搜索指定的对象,然后将一个索引返回到它当中(或,如果元素未找到为-1)。它使用对象的equals()方法,因此,如果对象不覆盖Object的equals()方法,它只比较对象引用,不比较对象内容。 public finalsynchronized Object elementAt (int index)-在指定的索引中返回元素。如果 index无效,它抛出ArrayIndexOutOfVoundsException。 public final synchronized void setElementAt (int index)-以指定对象在指定索引中替代指定元素。如果 index无效,它抛出ArrayIndexOutOfVoundsException。 public final synchronized void removeElementAt (int index)-删除指定索引中的元素。如果 index无效,它抛出ArrayIndexOutOfVoundsException。 public final synchronized void addElement (Object obj)-附加指定对象作为矢量的最后元素。 public final synchronized void insertElementAt (Object obj,int index)-插入指定对象作为指定索引中的一个元素,上移具有同等或更大索引的所有元素。如果index无效,它抛出ArrayIndexOutOfVoundsException。 6.17.5 向量Vector模板例子: 下述模板可以用来附加不同的元素类型到矢量中并打印出矢量元素: 注-这个程序使用的方法来自本模块中前面讨论的类。 import java.util.*; public class MyVector extends Vector { public MyVector() { super(1,1); // storage capacity & capacityIncrement } public void addInt(int i) { addElement(new Integer(i)); // addElement requires // Object arg } public void addFloat(float f) { addElement(new Float(f)); } public void addString(String s) { addElement(s); } public void addCharArray(char a[]) { addElement(a); } public void printVector() { Object o; int length = size(); // compare with capacity() System.out.println("Number of vector elements is " + length+ " and they are:"); for (int i = 0; i < length; i++) { o = elementAt(i); if (o instanceof char[]) { //An array's toString() method does not print what we want. System.out.println(String.copyValueOf((char[]) o)); } else System.out.println(o.toString()); } } public static void main(String args[]) { MyVector v = new MyVector() ; int digit = 5; float real = 3.14f; char letters[] = { 'a', 'b', 'c', 'd'}; String s = new String ("Hi there!"); v.addInt(digit); v.addFloat(real); v.addString(s); v.addCharArray(letters); v.printVector(); } } 这个程序产生下列输出: $ java MyVector Number of vector elements is 4 and are: 5 3.14 Hi there! Abcd 第十八节 反射API 反射API 可以用作 构造新类实例和新数组 访问并修改对象和类的字段 调用对象和类中的方法 访问并修改数组的元素   Java反射API提供一套类,可以用它们来决定一个类文件的变量和方法。因为被共同用于动态发现和执行代码的目的,因此API可以被用于: 构造新类实例和新数组 访问并修改对象和类的字段 调用对象和类中的方法 访问并修改数组的元素 只要安全策略允许,这些操作是可能的。在需要运行时检索并处理信息的情况下,反射API是有用的。例如,如果正在编写一个Java软件解释程序或调试程序,可以使用它。 第十九节 反射API特征 反射API特征 Java.lang.Class Java.lang.reflect.Field Java.lang.reflect.Method Java.lang.reflect.Array Java.lang.reflect.Constructor   定义类和方法的核心反射API的主要特征如下: Java.lang.Class类提供方法,该方法可获得有关类及其字段、构造函数以及方法的信息。 Java.lang.reflect.Field提供方法,该方法设定/获得有关类中的字段的信息。 Java.lang.reflect.Method提供方法,该方法访问并调用类中的方法,并获得它们的签名。 Java.lang.reflect.Array能使数组对象自省。 Java.lang.reflect.Constructor提供反射访问到构造函数。 第二十节 反射API安全模型 反射API安全模型 Java安全管理器一个类接一个类地控制对核心API的访问。 当下述情况发生时,标准的Java编程语言访问控制得到加强: Field被用来获得或设定一个字段值 Method被用来调用一个方法 Constructor被用来创建并初始化一个新的类的实例   Java安全管理器一个类接一个类地控制对核心API的访问。当下述情况发生时,标准的Java编程语言访问控制得到加强: Field被用来获得或设定一个字段值 Method被用来调用一个方法 Constructor被用来创建并初始化一个新的类的实例 练习:用高级语言特征工作 练习目的—使用银行帐户模型和采用高级面向对象特征,如:内部类,矢量类和接口等,重写、编译并运行三个程序。 一、准备 为了成功地完成该实验,必须熟悉本模块及前面模块中所讲的面向对象概念。 二、任务 一级实验:修改银行帐户问题 定义只包含两个方法deposit和withdraw的接口Personal。 从模块5中,运用Personal接口来定义一套不同的帐户类型,重新定义类Account.Java。它必须能处理个人帐户,进一步分成支票和存款两个帐户。 设计并开发提供保护的方法。例如,如果一个客户有一个存款和支票帐户,须确保支票帐户受存款帐户保护。 二级实验:使用内部类 创建一个叫做BasicArray的类,声明并初始化一个叫做thisArray的数组,它包含四个整数。 创建一个名为Factorial的类,它包含一个计算它的参数的阶乘的方法。 从BasicArray的主要方法创建Factorial类的一个实例,然后调用其方法来计算四个整数中每一个的阶乘。 编译并测试该程序。 将Factorial类中的所有东西都移到BasicArray类中。Factorial现在就是BasicArray的一个内部类。 编译并测试该程序。 三级实验:将find和delete方法附加到MyVector类中 将Find方法附加到MyVector类中,它将返回被作为参数传递的元素的位置。 如果未发现该参数,让方法返回-1。 例如: $ java MyVectorFind 3.14 3.14 is located at index 1 Number of vector elements is 4 and are: 5 3.14 Hi there! abcd $ java MyVectorFind c args[0]=c, not found in vector Number of vector elements is 4 and are: 5 3.14 Hi there! abcd 将delete方法附加到MyVector类中,该类将所有与参数相配的元素移走。 方法必须返回true或false:如果删除成功,为true;否则为false(元素存在或不存在于矢量中)。 例如: $ java MyVectorDelete 3.14 Elements 3.14 successfully deleted from vector. Number of vector elements is 3 and are: 5 Hi there! abcd 三、练习总结 讨论—花几分钟来讨论实验练习中遇到的经验、问题或发现。 经验 解释 总结 应用 四、检查进步情况 在继续学习下个模块前,检查一下你确实能: 描述static变量、方法和初始化程序 描述final类、方法和变量 列出访问控制级别 确认降级类并解释如何从JDK1.0升迁到JDK1.1、JDK1.2。 在Java软件程序中,确认: static方法和变量 public,private,protected和缺省变量 使用abstract类和方法 解释如何及何时使用内部类 解释如何及何时使用接口 描述= =和equals()之间的不同 五、思考 Java编程语言具有什么特征,从而可以直接处理运行时错误情况?