对象容纳
“如果一个程序只含有数量固定的对象,而且已知它们的存在时间,那么这个程序可以说是相当简单的。”
通常,我们的程序需要根据程序运行时才知道的一些标准创建新对象。若非程序正式运行,否则我们根本不知道自己到底需要多少数量的对象,甚至不知道它们的准确类型。为了满足常规编程的需要,我们要求能在任何时候、任何地点创建任意数量的对象。所以不可依赖一个已命名的句柄来容纳自己的每一个对象,就象下面这样:
MyObject myHandle;
因为根本不知道自己实际需要多少这样的东西。
为解决这个非常关键的问题,Java 提供了容纳对象(或者对象的句柄)的多种方式。其中内建的类型是数组。此外,Java 的工具(实用程序)库提供了一些“集合类”(亦称作“容器类”,但该术语已由AWT使用,所以这里仍采用“集合”这一称呼)。利用这些集合类,我们可以容纳乃至操纵自己的对象。
我们已知道自己该如何定义及初始化一个数组。数组只是容纳对象的一种方式。但由于还有其他大量方法可容纳数组,所以是哪些地方使数组显得如此特别呢?
有两方面的问题将数组与其他集合类型区分开来:效率和类型。对于Java 来说,为保存和访问一系列对象(实际是对象的句柄)数组,最有效的方法莫过于数组。数组实际代表一个简单的线性序列,它使得元素的访问速度非常快,但我们却要为这种速度付出代价:创建一个数组对象时,它的大小是固定的,而且不可在那个数组对象的“存在时间”内发生改变。可创建特定大小的一个数组,然后假如用光了存储空间,就再创建一个新数组,将所有句柄从旧数组移到新数组。这属于“矢量”(Vector)类的行为。然而,由于为这种大小的灵活性要付出较大的代价,所以我们认为矢量的效率并没有数组高。
C++的矢量类知道自己容纳的是什么类型的对象,但同 Java 的数组相比,它却有一个明显的缺点:C++矢量类的operator[]不能进行范围检查,所以很容易超出边界(然而,它可以查询 vector 有多大,而且at()方法确实能进行范围检查)。在Java 中,无论使用的是数组还是集合,都会进行范围检查——若超过边界,就会获得一个RuntimeException(运行期违例)错误。这类违例指出的是一个程序员错误,所以不需要在代码中检查它。在另一方面,由于 C++的vector不进行范围检查,所以访问速度较快——在Java 中,由于对数组和集合都要进行范围检查,所以对性能有一定的影响。
另外几种常见的集合类:Vector(矢量)、Stack(堆栈)以及Hashtable(散列表)。这些类都涉及对对象的处理——好象它们没有特定的类型。换言之,它们将其当作 Object 类型处理(Object类型是Java 中所有类的“根”类)。从某个角度看,这种处理方法是非常合理的:我们仅需构建一个集合,然后任何Java 对象都可以进入那个集合(除基本数据类型外——可用Java 的基本类型封装类将其作为常数置入集合,或者将其封装到自己的类内,作为可以变化的值使用)。这再一次反映了数组优于常规集合:创建一个数组时,可令其容纳一种特定的类型。这意味着可进行编译期类型检查,预防自己设置了错误的类型,或者错误指定了准备提取的类型。当然,在编译期或者运行期,Java 会防止我们将不当的消息发给一个对象。
所以我们不必考虑自己的哪种做法更加危险,只要编译器能及时地指出错误,同时在运行期间加快速度,目的也就达到了。此外,用户很少会对一次违例事件感到非常惊讶的。
考虑到执行效率和类型检查,应尽可能地采用数组。然而,当我们试图解决一个更常规的问题时,数组的局限也可能显得非常明显。在研究过数组以后,将把重点放到Java 提供的集合类身上。
无论使用的数组属于什么类型,数组标识符实际都是指向真实对象的一个句柄。那些对象本身是在内存“堆”里创建的。堆对象既可“隐式”创建(即默认产生),亦可“显式”创建(即明确指定,用一个new表达式)。堆对象的一部分(实际是我们能访问的唯一字段或方法)是只读的length(长度)成员,它告诉我们那个数组对象里最多能容纳多少元素。对于数组对象,“[]”语法是我们能采用的唯一另类访问方法。
下面这个例子展示了对数组进行初始化的不同方式,以及如何将数组句柄分配给不同的数组对象。它也揭示出对象数组和基本数据类型数组在使用方法上几乎是完全一致的。唯一的差别在于对象数组容纳的是句柄,而基本数据类型数组容纳的是具体的数值:
示例如下:
class Weeble {
} // A small mythical creature
public class ArraySize {
public static void main(String[] args) {
// Arrays of objects:
Weeble[] a; // Null handle
Weeble[] b = new Weeble[5]; // Null handles
Weeble[] c = new Weeble[4];
for (int i = 0; i < c.length; i++)
c[i] = new Weeble();
Weeble[] d = { new Weeble(), new Weeble(), new Weeble() };
// Compile error: variable a not initialized:
// !System.out.println("a.length=" + a.length);
System.out.println("b.length = " + b.length);
// The handles inside the array are
// automatically initialized to null:
for (int i = 0; i < b.length; i++)
System.out.println("b[" + i + "]=" + b[i]);
System.out.println("c.length = " + c.length);
System.out.println("d.length = " + d.length);
a = d;
System.out.println("a.length = " + a.length);
// Java 1.1 initialization syntax:
a = new Weeble[] { new Weeble(), new Weeble() };
System.out.println("a.length = " + a.length);
// Arrays of primitives:
int[] e; // Null handle
int[] f = new int[5];
int[] g = new int[4];
for (int i = 0; i < g.length; i++)
g[i] = i * i;
int[] h = { 11, 47, 93 };
// Compile error: variable e not initialized:
// !System.out.println("e.length=" + e.length);
System.out.println("f.length = " + f.length);
// The primitives inside the array are
// automatically initialized to zero:
for (int i = 0; i < f.length; i++)
System.out.println("f[" + i + "]=" + f[i]);
System.out.println("g.length = " + g.length);
System.out.println("h.length = " + h.length);
e = h;
System.out.println("e.length = " + e.length);
// Java 1.1 initialization syntax:
e = new int[] { 1, 2 };
System.out.println("e.length = " + e.length);
}
} // /:~
输出如下:
b.length = 5
b[0]=null
b[1]=null
b[2]=null
b[3]=null
b[4]=null
c.length = 4
d.length = 3
a.length = 3
a.length = 2
f.length = 5
f[0]=0
f[1]=0
f[2]=0
f[3]=0
f[4]=0
g.length = 4
h.length = 3
e.length = 3
e.length = 2
其中,数组 a只是初始化成一个 null 句柄。此时,编译器会禁止我们对这个句柄作任何实际操作,除非已正确地初始化了它。数组 b被初始化成指向由 Weeble 句柄构成的一个数组,但那个数组里实际并未放置任何Weeble对象。然而,我们仍然可以查询那个数组的大小,因为b指向的是一个合法对象。这也为我们带来了一个难题:不可知道那个数组里实际包含了多少个元素,因为 length只告诉我们可将多少元素置入那个数组。换言之,我们只知道数组对象的大小或容量,不知其实际容纳了多少个元素。尽管如此,由于数组对象
在创建之初会自动初始化成null,所以可检查它是否为 null,判断一个特定的数组“空位”是否容纳一个对象。类似地,由基本数据类型构成的数组会自动初始化成零(针对数值类型)、null(字符类型)或者false(布尔类型)。
数组c 显示出我们首先创建一个数组对象,再将Weeble 对象赋给那个数组的所有“空位”。数组d 揭示出“集合初始化”语法,从而创建数组对象(用 new命令明确进行,类似于数组c),然后用Weeble 对象进行初始化,全部工作在一条语句里完成。
下面这个表达式:
a = d;
向我们展示了如何取得同一个数组对象连接的句柄,然后将其赋给另一个数组对象,就象我们针对对象句柄的其他任何类型做的那样。现在,a和d 都指向内存堆内同样的数组对象。
Java 1.1 加入了一种新的数组初始化语法,可将其想象成“动态集合初始化”。由 d 采用的Java 1.0 集合初始化方法则必须在定义d 的同时进行。但若采用 Java 1.1 的语法,却可以在任何地方创建和初始化一个数组对象。例如,假设hide()方法用于取得一个Weeble 对象数组,那么调用它时传统的方法是:
hide(d);
但在Java 1.1中,亦可动态创建想作为参数传递的数组,如下所示:
hide(new Weeble[] {new Weeble(), new Weeble() });
这一新式语法使我们在某些场合下写代码更方便了。
对于由基本数据类型构成的数组,它们的运作方式与对象数组极为相似,只是前者直接包容了基本类型的数据值。
集合类只能容纳对象句柄。但对一个数组,却既可令其直接容纳基本类型的数据,亦可容纳指向对象的句柄。利用象 Integer、Double之类的“封装器”类,可将基本数据类型的值置入一个集合里。用于基本数据类型的封装器类只是在某些场合下才能发挥作用。
无论将基本类型的数据置入数组,还是将其封装进入位于集合的一个类内,都涉及到执行效率的问题。显然,若能创建和访问一个基本数据类型数组,那么比起访问一个封装数据的集合,前者的效率会高出许多。
当然,假如准备一种基本数据类型,同时又想要集合的灵活性(在需要的时候可自动扩展,腾出更多的空间),就不宜使用数组,必须使用由封装的数据构成的一个集合。大家或许认为针对每种基本数据类型,都应有一种特殊类型的Vector。但Java 并未提供这一特性。某些形式的建模机制或许会在某一天帮助 Java 更好地解决这个问题。这个是 C++比Java 做得好的一个地方,因为C++通过template 关键字提供了对“参数化类型”的支持。
假定我们现在想写一个方法,同时不希望它仅仅返回一样东西,而是想返回一系列东西。此时,象C 和C++这样的语言会使问题复杂化,因为我们不能返回一个数组,只能返回指向数组的一个指针。这样就非常麻烦,因为很难控制数组的“存在时间”,它很容易造成内存“漏洞”的出现。
Java 采用的是类似的方法,但我们能“返回一个数组”。当然,此时返回的实际仍是指向数组的指针。但在Java 里,我们永远不必担心那个数组的是否可用——只要需要,它就会自动存在。而且垃圾收集器会在我们完成后自动将其清除。
请思考如何返回一个字串数组:
public class IceCream {
static String[] flav = { "Chocolate", "Strawberry", "Vanilla Fudge Swirl",
"Mint Chip", "Mocha Almond Fudge", "Rum Raisin", "Praline Cream",
"Mud Pie" };
static String[] flavorSet(int n) {
// Force it to be positive & within bounds:
n = Math.abs(n) % (flav.length + 1);
String[] results = new String[n];
int[] picks = new int[n];
for (int i = 0; i < picks.length; i++)
picks[i] = -1;
for (int i = 0; i < picks.length; i++) {
retry: while (true) {
int t = (int) (Math.random() * flav.length);
for (int j = 0; j < i; j++)
if (picks[j] == t)
continue retry;
picks[i] = t;
results[i] = flav[t];
break;
}
}
return results;
}
public static void main(String[] args) {
for (int i = 0; i < 20; i++) {
System.out.println("flavorSet(" + i + ") = ");
String[] fl = flavorSet(flav.length);
for (int j = 0; j < fl.length; j++)
System.out.println("\t" + fl[j]);
}
}
} // /:~
输出:
flavorSet(0) =
Chocolate
Vanilla Fudge Swirl
Praline Cream
Mint Chip
Strawberry
Mocha Almond Fudge
Rum Raisin
Mud Pie
flavorSet(1) =
Rum Raisin
Vanilla Fudge Swirl
Mud Pie
Chocolate
Strawberry
Mint Chip
Praline Cream
Mocha Almond Fudge
flavorSet(2) =
Vanilla Fudge Swirl
Rum Raisin
Mocha Almond Fudge
Praline Cream
Mint Chip
Mud Pie
Strawberry
Chocolate
flavorSet(3) =
Mocha Almond Fudge
Praline Cream
Strawberry
Rum Raisin
Mint Chip
Chocolate
Vanilla Fudge Swirl
Mud Pie
flavorSet(4) =
Mocha Almond Fudge
Mint Chip
Vanilla Fudge Swirl
Praline Cream
Strawberry
Rum Raisin
Mud Pie
Chocolate
flavorSet(5) =
Vanilla Fudge Swirl
Mud Pie
Chocolate
Praline Cream
Strawberry
Rum Raisin
Mocha Almond Fudge
Mint Chip
flavorSet(6) =
Rum Raisin
Chocolate
Mud Pie
Praline Cream
Mint Chip
Vanilla Fudge Swirl
Strawberry
Mocha Almond Fudge
flavorSet(7) =
Vanilla Fudge Swirl
Praline Cream
Mocha Almond Fudge
Mint Chip
Chocolate
Strawberry
Mud Pie
Rum Raisin
flavorSet(8) =
Mocha Almond Fudge
Rum Raisin
Mud Pie
Praline Cream
Chocolate
Mint Chip
Strawberry
Vanilla Fudge Swirl
flavorSet(9) =
Mint Chip
Chocolate
Praline Cream
Vanilla Fudge Swirl
Strawberry
Mocha Almond Fudge
Mud Pie
Rum Raisin
flavorSet(10) =
Chocolate
Mud Pie
Strawberry
Mocha Almond Fudge
Rum Raisin
Mint Chip
Praline Cream
Vanilla Fudge Swirl
flavorSet(11) =
Rum Raisin
Chocolate
Vanilla Fudge Swirl
Strawberry
Praline Cream
Mocha Almond Fudge
Mint Chip
Mud Pie
flavorSet(12) =
Chocolate
Mocha Almond Fudge
Mud Pie
Vanilla Fudge Swirl
Strawberry
Rum Raisin
Mint Chip
Praline Cream
flavorSet(13) =
Mocha Almond Fudge
Chocolate
Praline Cream
Strawberry
Mint Chip
Vanilla Fudge Swirl
Rum Raisin
Mud Pie
flavorSet(14) =
Mud Pie
Mint Chip
Praline Cream
Rum Raisin
Chocolate
Vanilla Fudge Swirl
Strawberry
Mocha Almond Fudge
flavorSet(15) =
Chocolate
Vanilla Fudge Swirl
Mocha Almond Fudge
Praline Cream
Strawberry
Rum Raisin
Mint Chip
Mud Pie
flavorSet(16) =
Mint Chip
Mud Pie
Chocolate
Mocha Almond Fudge
Vanilla Fudge Swirl
Praline Cream
Strawberry
Rum Raisin
flavorSet(17) =
Vanilla Fudge Swirl
Strawberry
Mint Chip
Mud Pie
Rum Raisin
Mocha Almond Fudge
Chocolate
Praline Cream
flavorSet(18) =
Chocolate
Mint Chip
Mud Pie
Mocha Almond Fudge
Vanilla Fudge Swirl
Strawberry
Praline Cream
Rum Raisin
flavorSet(19) =
Chocolate
Rum Raisin
Mint Chip
Vanilla Fudge Swirl
Mud Pie
Strawberry
Praline Cream
Mocha Almond Fudge
flavorSet()方法创建了一个名为results的String 数组。该数组的大小为 n——具体数值取决于我们传递给方法的自变量。随后,它从数组 flav 里随机挑选一些“香料”(Flavor),并将它们置入 results里,并最终返回results。返回数组与返回其他任何对象没什么区别——最终返回的都是一个句柄。至于数组到底是在flavorSet()里创建的,还是在其他什么地方创建的,这个问题并不重要,因为反正返回的仅是一个句柄。一旦我们的操作完成,垃圾收集器会自动关照数组的清除工作。而且只要我们需要数组,它就会乖乖地听候调遣。
另一方面,注意当flavorSet()随机挑选香料的时候,它需要保证以前出现过的一次随机选择不会再次出现。为达到这个目的,它使用了一个无限while 循环,不断地作出随机选择,直到发现未在picks 数组里出现过的一个元素为止(当然,也可以进行字串比较,检查随机选择是否在 results数组里出现过,但字串比较的效率比较低)。若成功,就添加这个元素,并中断循环(break),再查找下一个(i 值会递增)。但假若t 是一个已在 picks 里出现过的数组,就用标签式的continue 往回跳两级,强制选择一个新 t。用一个调试程序可以很清楚地看到这个过程。
main()能显示出20个完整的香料集合,所以我们看到 flavorSet()每次都用一个随机顺序选择香料。为体会这一点,最简单的方法就是将输出重导向进入一个文件,然后直接观看这个文件的内容。
为容纳一组对象,最适宜的选择应当是数组。而且假如容纳的是一系列基本数据类型,更是必须采用数组。
当我们编写程序时,通常并不能确切地知道最终需要多少个对象。有些时候甚至想用更复杂的方式来保存对象。为解决这个问题,Java 提供了四种类型的“集合类”:Vector(矢量)、BitSet(位集)、Stack(堆栈)以及Hashtable(散列表)。与拥有集合功能的其他语言相比,尽管这儿的数量显得相当少,但仍然能用它们解决数量惊人的实际问题。
这些集合类具有形形色色的特征。例如,Stack 实现了一个 LIFO(先入先出)序列,而 Hashtable 是一种“关联数组”,允许我们将任何对象关联起来。除此以外,所有Java 集合类都能自动改变自身的大小。所以,我们在编程时可使用数量众多的对象,同时不必担心会将集合弄得有多大。
使用Java 集合的“缺点”是在将对象置入一个集合时丢失了类型信息。之所以会发生这种情况,是由于当初编写集合时,那个集合的程序员根本不知道用户到底想把什么类型置入集合。若指示某个集合只允许特定的类型,会妨碍它成为一个“常规用途”的工具,为用户带来麻烦。
为解决这个问题,集合实际容纳的是类型为Object 的一些对象的句柄。这种类型当然代表Java 中的所有对象,因为它是所有类的根。当然,也要注意这并不包括基本数据类型,因为它们并不是从“任何东西”继承来的。这是一个很好的方案,只是不适用下述场合:
(1) 将一个对象句柄置入集合时,由于类型信息会被抛弃,所以任何类型的对象都可进入我们的集合——即便特别指示它只能容纳特定类型的对象。举个例子来说,虽然指示它只能容纳猫,但事实上任何人都可以把一条狗扔进来。
(2) 由于类型信息不复存在,所以集合能肯定的唯一事情就是自己容纳的是指向一个对象的句柄。正式使用它之前,必须对其进行造型,使其具有正确的类型。 值得欣慰的是,Java 不允许人们滥用置入集合的对象。假如将一条狗扔进一个猫的集合,那么仍会将集合内的所有东西都看作猫,所以在使用那条狗时会得到一个“违例”错误。在同样的意义上,假若试图将一条狗的句柄“造型”到一只猫,那么运行期间仍会得到一个“违例”错误。
下面是个例子:
import java.util.*;
class Cat {
private int catNumber;
Cat(int i) {
catNumber = i;
}
void print() {
System.out.println("Cat #" + catNumber);
}
}
class Dog {
private int dogNumber;
Dog(int i) {
dogNumber = i;
}
void print() {
System.out.println("Dog #" + dogNumber);
}
}
public class CatsAndDogs {
public static void main(String[] args) {
Vector cats = new Vector();
for (int i = 0; i < 7; i++)
cats.addElement(new Cat(i));
// Not a problem to add a dog to cats:
cats.addElement(new Dog(7));
for (int i = 0; i < cats.size(); i++)
((Cat) cats.elementAt(i)).print();
// Dog is detected only at run-time
}
} // /:~
输出:
Cat #0
Cat #1
Cat #2
Cat #3
Cat #4
Cat #5
Cat #6
Exception in thread "main" java.lang.ClassCastException: Dog incompatible with Cat
at java.lang.ClassCastException.
at CatsAndDogs.main(CatsAndDogs.java:35)
包含一个错误。
可以看出,Vector的使用是非常简单的:先创建一个,再用 addElement()置入对象,以后用 elementAt()取得那些对象(注意Vector 有一个 size()方法,可使我们知道已添加了多少个元素,以便防止误超边界,造成违例错误)。
Cat和Dog类都非常浅显——除了都是“对象”之外,它们并无特别之处(倘若不明确指出从什么类继承,就默认为从 Object继承。所以我们不仅能用Vector方法将 Cat对象置入这个集合,也能添加Dog对象,同时不会在编译期和运行期得到任何出错提示。用Vector 方法elementAt()获取原本认为是Cat 的对象时,实际获得的是指向一个Object 的句柄,必须将那个对象造型为Cat。随后,需要将整个表达式用括号封闭起来,在为Cat调用print()方法之前进行强制造型;否则就会出现一个语法错误。在运行期间,如果试图将Dog对象造型为 Cat,就会得到一个违例。
这些处理的意义都非常深远。尽管显得有些麻烦,但却获得了安全上的保证。我们从此再难偶然造成一些隐藏得深的错误。若程序的一个部分(或几个部分)将对象插入一个集合,但我们只是通过一次违例在程序的某个部分发现一个错误的对象置入了集合,就必须找出插入错误的位置。当然,可通过检查代码达到这个目的,但这或许是最笨的调试工具。另一方面,我们可从一些标准化的集合类开始自己的编程。尽管它们在功能上存在一些不足,且显得有些笨拙,但却能保证没有隐藏的错误。
在某些情况下,程序似乎正确地工作,不造型回我们原来的类型。第一种情况是相当特殊的:String 类从编译器获得了额外的帮助,使其能够正常工作。只要编译器期待的是一个String 对象,但它没有得到一个,就会自动调用在Object 里定义、并且能够由任何Java 类覆盖的toString()方法。这个方法能生成满足要求的String对象,然后在我们需要的时候使用。
因此,为了让自己类的对象能显示出来,要做的全部事情就是覆盖toString()方法,
如下例所示:
import java.util.*;
class Mouse {
private int mouseNumber;
Mouse(int i) {
mouseNumber = i;
}
// Magic method:
public String toString() {
return "This is Mouse #" + mouseNumber;
}
void print(String msg) {
if (msg != null)
System.out.println(msg);
System.out.println("Mouse number " + mouseNumber);
}
}
class MouseTrap {
static void caughtYa(Object m) {
Mouse mouse = (Mouse) m; // Cast from Object
mouse.print("Caught one!");
}
}
public class WorksAnyway {
public static void main(String[] args) {
Vector mice = new Vector();
for (int i = 0; i < 3; i++)
mice.addElement(new Mouse(i));
for (int i = 0; i < mice.size(); i++) {
// No cast necessary, automatic call
// to Object.toString():
System.out.println("Free mouse: " + mice.elementAt(i));
MouseTrap.caughtYa(mice.elementAt(i));
}
}
} // /:~
输出如下:
Free mouse: This is Mouse #0
Caught one!
Mouse number 0
Free mouse: This is Mouse #1
Caught one!
Mouse number 1
Free mouse: This is Mouse #2
Caught one!
Mouse number 2
可在Mouse 里看到对toString()的重定义代码。在main()的第二个for 循环中,可发现下述语句:
System.out.println("Free mouse: " + mice.elementAt(i));
在“+”后,编译器预期看到的是一个String 对象。elementAt()生成了一个 Object,所以为获得希望的String,编译器会默认调用toString()。但不幸的是,只有针对String 才能得到象这样的结果;其他任何类型都不会进行这样的转换。
隐藏造型的第二种方法已在Mousetrap 里得到了应用。caughtYa()方法接收的不是一个Mouse,而是一个Object。随后再将其造型为一个Mouse。当然,这样做是非常冒失的,因为通过接收一个 Object,任何东西都可以传递给方法。然而,假若造型不正确——如果我们传递了错误的类型——就会在运行期间得到一个违例错误。这当然没有在编译期进行检查好,但仍然能防止问题的发生。注意在使用这个方法时毋需进行造型:
MouseTrap.caughtYa(mice.elementAt(i));
一个更“健壮”的方案是用Vector创建一个新类,使其只接收我们指定的类型,也只生成我们希望的类型。
如下所示:
import java.util.*;
class Gopher {
private int gopherNumber;
Gopher(int i) {
gopherNumber = i;
}
void print(String msg) {
if (msg != null)
System.out.println(msg);
System.out.println("Gopher number " + gopherNumber);
}
}
class GopherTrap {
static void caughtYa(Gopher g) {
g.print("Caught one!");
}
}
class GopherVector {
private Vector v = new Vector();
public void addElement(Gopher m) {
v.addElement(m);
}
public Gopher elementAt(int index) {
return (Gopher) v.elementAt(index);
}
public int size() {
return v.size();
}
public static void main(String[] args) {
GopherVector gophers = new GopherVector();
for (int i = 0; i < 3; i++)
gophers.addElement(new Gopher(i));
for (int i = 0; i < gophers.size(); i++)
GopherTrap.caughtYa(gophers.elementAt(i));
}
} // /:~
输出如下:
Caught one!
Gopher number 0
Caught one!
Gopher number 1
Caught one!
Gopher number 2
GopherVector 类有一个类型为Vector的private成员(从Vector继承有些麻烦,理由稍后便知),而且方法也和Vector 类似。然而,它不会接收和产生普通Object,只对 Gopher对象感兴趣。
由于GopherVector只接收一个 Gopher(地鼠),所以假如我们使用:
gophers.addElement(new Pigeon());
就会在编译期间获得一条出错消息。采用这种方式,尽管从编码的角度看显得更令人沉闷,但可以立即判断出是否使用了正确的类型。
注意在使用 elementAt()时不必进行造型——它肯定是一个Gopher。
3. 参数化类型
这类问题并不是孤立的——我们许多时候都要在其他类型的基础上创建新类型。此时,在编译期间拥有特定的类型信息是非常有帮助的。这便是“参数化类型”的概念。在C++中,它由语言通过“模板”获得了直接支持。至少,Java 保留了关键字generic,期望有一天能够支持参数化类型。但我们现在无法确定这一天何时会来临。
在任何集合类中,必须通过某种方法在其中置入对象,再用另一种方法从中取得对象。容纳各种各样的对象正是集合的首要任务。在Vector 中,addElement()便是我们插入对象采用的方法,而 elementAt()是提取对象的唯一方法。Vector非常灵活,我们可在任何时候选择任何东西,并可使用不同的索引选择多个元素。
若从更高的角度看这个问题,就会发现它的一个缺陷:需要事先知道集合的准确类型,否则无法使用。这一点似乎没什么关系。但假若最开始决定使用Vector,后来在程序中又决定(考虑执行效率的原因)改变成一个 List(属于Java1.2 集合库的一部分),这时又该如何做呢?
可利用“反复器”(Iterator)的概念达到这个目的。它可以是一个对象,作用是遍历一系列对象,并选择那个序列中的每个对象,同时不让客户程序员知道或关注那个序列的基础结构。此外,我们通常认为反复器是一种“轻量级”对象;也就是说,创建它只需付出极少的代价。但也正是由于这个原因,我们常发现反复器存在一些似乎很奇怪的限制。例如,有些反复器只能朝一个方向移动。
Java 的Enumeration(枚举)便是具有这些限制的一个反复器的例子。
除下面这些外,不可再用它做其他任何事情:
(1) 用一个名为 elements()的方法要求集合为我们提供一个 Enumeration。我们首次调用它的 nextElement()时,这个Enumeration 会返回序列中的第一个元素。
(2) 用nextElement()获得下一个对象。
(3) 用hasMoreElements()检查序列中是否还有更多的对象。
只可用Enumeration 做这些事情,不能再有更多。它属于反复器一种简单的实现方式,但功能依然十分强大。为体会它的运作过程看到使用了一个“枚举”:
import java.util.*;
class Cat2 {
private int catNumber;
Cat2(int i) {
catNumber = i;
}
void print() {
System.out.println("Cat number " + catNumber);
}
}
class Dog2 {
private int dogNumber;
Dog2(int i) {
dogNumber = i;
}
void print() {
System.out.println("Dog number " + dogNumber);
}
}
public class CatsAndDogs2 {
public static void main(String[] args) {
Vector cats = new Vector();
for (int i = 0; i < 7; i++)
cats.addElement(new Cat2(i));
// Not a problem to add a dog to cats:
cats.addElement(new Dog2(7));
Enumeration e = cats.elements();
while (e.hasMoreElements())
( (Cat2) e.nextElement()).print();
// ((Cat2) e.nextElement()).print();
// Dog is detected only at run-time
}
} // /:~
输出如下:
Cat number 0
Cat number 1
Cat number 2
Cat number 3
Cat number 4
Cat number 5
Cat number 6
Exception in thread "main" java.lang.ClassCastException: Dog2 incompatible with Cat2
at java.lang.ClassCastException.
at CatsAndDogs2.main(CatsAndDogs2.java:36)
再看看另一个例子,让我们创建一个常规用途的打印方法:
import java.util.*;
class Hamster {
private int hamsterNumber;
Hamster(int i) {
hamsterNumber = i;
}
public String toString() {
return "This is Hamster #" + hamsterNumber;
}
}
class Printer {
static void printAll(Enumeration e) {
while(e.hasMoreElements())
System.out.println(
e.nextElement().toString());
}
}
public class HamsterMaze {
public static void main(String[] args) {
Vector v = new Vector();
for(int i = 0; i < 3; i++)
v.addElement(new Hamster(i));
Printer.printAll(v.elements());
}
} ///:
输出如下:
This is Hamster #0
This is Hamster #1
This is Hamster #2
注意其中没有与序列类型有关的信息。我们拥有的全部东西便是Enumeration。为了解有关序列的情况,一个Enumeration 便足够了:可取得下一个对象,亦可知道是否已抵达了末尾。取得一系列对象,然后在其中遍历,从而执行一个特定的操作——这是一个颇有价值的编程概念。
这个看似特殊的例子甚至可以更为通用,因为它使用了常规的 toString()方法(之所以称为常规,是由于它属于Object类的一部分)。下面是调用打印的另一个方法(尽管在效率上可能会差一些):
System.out.println("" + e.nextElement());
它采用了封装到Java 内部的“自动转换成字串”技术。一旦编译器碰到一个字串,后面跟随一个“+”,就会希望后面又跟随一个字串,并自动调用toString()。在Java 1.1中,第一个字串是不必要的;所有对象都会转换成字串。亦可对此执行一次造型,获得与调用toString()同样的效果:
System.out.println((String)e.nextElement())
但我们想做的事情通常并不仅仅是调用Object方法,所以会再度面临类型造型的问题。对于自己感兴趣的类型,必须假定自己已获得了一个Enumeration,然后将结果对象造型成为那种类型(若操作错误,会得到运行期违例)
标准Java 1.0 和1.1库配套提供了非常少的一系列集合类。但对于自己的大多数编程要求,它们基本上都能胜任。正如大家到本章末尾会看到的,Java 1.2 提供的是一套重新设计过的大型集合库。
Vector的用法很简单,这已在前面的例子中得到了证明。尽管我们大多数时候只需用addElement()插入对象,用elementAt()一次提取一个对象,并用elements()获得对序列的一个“枚举”。但仍有其他一系列方法是非常有用的。同我们对于Java 库惯常的做法一样。
Java 标准集合里包含了toString()方法,所以它们能生成自己的 String表达方式,包括它们容纳的对象。在Vector 中,toString()会在Vector 的各个元素中步进和遍历,并为每个元素调用 toString()。假定我们现在想打印出自己类的地址。看起来似乎简单地引用 this 即可(特别是C++程序员有这样做的倾向):
import java.util.*;
public class CrashJava {
public String toString() {
return "CrashJava address: " + this + "\n";
}
public static void main(String[] args) {
Vector v = new Vector();
for (int i = 0; i < 10; i++)
v.addElement(new CrashJava());
System.out.println(v);
}
} // /:~
输出如下:
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at CrashJava.toString(CrashJava.java:5)
at java.lang.String.valueOf(String.java:1659)
at java.lang.StringBuilder.append(StringBuilder.java:194)
at java.util.AbstractCollection.toString(AbstractCollection.java:469)
at java.util.Vector.toString(Vector.java:1007)
at java.lang.String.valueOf(String.java:1659)
at com.ibm.jvm.io.ConsolePrintStream.getNewlinedString(ConsolePrintStream.java:345)
at com.ibm.jvm.io.ConsolePrintStream.getNewlinedString(ConsolePrintStream.java:329)
at com.ibm.jvm.io.ConsolePrintStream.println(ConsolePrintStream.java:319)
at CrashJava.main(CrashJava.java:12)
若只是简单地创建一个 CrashJava 对象,并将其打印出来,就会得到无穷无尽的一系列违例错误。然而,假如将CrashJava 对象置入一个Vector,并象这里演示的那样打印Vector,就不会出现什么错误提示,甚至连一个违例都不会出现。此时Java 只是简单地崩溃(但至少它没有崩溃我的操作系统)。
此时发生的是字串的自动类型转换。当我们使用下述语句时:
"CrashJava address: " + this
编译器就在一个字串后面发现了一个“+”以及好象并非字串的其他东西,所以它会试图将this 转换成一个字串。转换时调用的是 toString(),后者会产生一个递归调用。若在一个Vector 内出现这种事情,看起来堆栈就会溢出,同时违例控制机制根本没有机会作出响应。
若确实想在这种情况下打印出对象的地址,解决方案就是调用 Object的toString 方法。此时就不必加入this,只需使用 super.toString()。当然,采取这种做法也有一个前提:我们必须从 Object 直接继承,或者没有一个父类覆盖了 toString 方法。
PS:这里蛤蟆使用Vector也崩溃了,崩溃ing .
BitSet实际是由“二进制位”构成的一个 Vector。如果希望高效率地保存大量“开-关”信息,就应使用BitSet。它只有从尺寸的角度看才有意义;如果希望的高效率的访问,那么它的速度会比使用一些固有类型的数组慢一些。
此外,BitSet的最小长度是一个长整数(Long)的长度:64 位。这意味着假如我们准备保存比这更小的数据,如8 位数据,那么BitSet就显得浪费了。所以最好创建自己的类,用它容纳自己的标志位。
在一个普通的Vector 中,随我们加入越来越多的元素,集合也会自我膨胀。在某种程度上,BitSet 也不例外。也就是说,它有时会自行扩展,有时则不然。而且Java 的1.0 版本似乎在这方面做得最糟,它的BitSet表现十分差强人意(Java1.1已改正了这个问题)。下面这个例子展示了BitSet 是如何运作的:
import java.util.*;
public class Bits {
public static void main(String[] args) {
Random rand = new Random();
// Take the LSB of nextInt():
byte bt = (byte) rand.nextInt();
BitSet bb = new BitSet();
for (int i = 7; i >= 0; i--)
if (((1 << i) & bt) != 0)
bb.set(i);
else
bb.clear(i);
System.out.println("byte value: " + bt);
printBitSet(bb);
short st = (short) rand.nextInt();
BitSet bs = new BitSet();
for (int i = 15; i >= 0; i--)
if (((1 << i) & st) != 0)
bs.set(i);
else
bs.clear(i);
System.out.println("short value: " + st);
printBitSet(bs);
int it = rand.nextInt();
BitSet bi = new BitSet();
for (int i = 31; i >= 0; i--)
if (((1 << i) & it) != 0)
bi.set(i);
else
bi.clear(i);
System.out.println("int value: " + it);
printBitSet(bi);
// Test bitsets >= 64 bits:
BitSet b127 = new BitSet();
b127.set(127);
System.out.println("set bit 127: " + b127);
BitSet b255 = new BitSet(65);
b255.set(255);
System.out.println("set bit 255: " + b255);
BitSet b1023 = new BitSet(512);
// Without the following, an exception is thrown
// in the Java 1.0 implementation of BitSet:
// b1023.set(1023);
b1023.set(1024);
System.out.println("set bit 1023: " + b1023);
}
static void printBitSet(BitSet b) {
System.out.println("bits: " + b);
String bbits = new String();
for (int j = 0; j < b.size(); j++)
bbits += (b.get(j) ? "1" : "0");
System.out.println("bit pattern: " + bbits);
}
} // /:~
输出如下:
byte value: 15
bits: {0, 1, 2, 3}
bit pattern: 1111000000000000000000000000000000000000000000000000000000000000
short value: -27155
bits: {0, 2, 3, 5, 6, 7, 8, 10, 12, 15}
bit pattern: 1011011110101001000000000000000000000000000000000000000000000000
int value: -1260070390
bits: {1, 3, 9, 11, 12, 14, 15, 18, 21, 22, 23, 26, 28, 29, 31}
bit pattern: 0101000001011011001001110010110100000000000000000000000000000000
set bit 127: {127}
set bit 255: {255}
set bit 1023: {1024}
随机数字生成器用于创建一个随机的byte、short 和int。每一个都会转换成BitSet内相应的位模型。此时一切都很正常,因为BitSet 是64 位的,所以它们都不会造成最终尺寸的增大。但在 Java 1.0 中,一旦BitSet大于64 位,就会出现一些令人迷惑不解的行为。假如我们设置一个只比BitSet 当前分配存储空间大出1 的一个位,它能够正常地扩展。但一旦试图在更高的位置设置位,同时不先接触边界,就会得到一个恼人的违例。这正是由于 BitSet在Java 1.0 里不能正确扩展造成的。本例创建了一个512 位的BitSet。构建器分配的存储空间是位数的两倍。所以假如设置位 1024 或更高的位,同时没有先设置位1023,就会在Java
1.0里得到一个违例。但幸运的是,这个问题已在 Java 1.1 得到了改正。所以如果是为Java 1.0 写代码,请尽量避免使用BitSet。
Stack 有时也可以称为“后入先出”(LIFO)集合。换言之,我们在堆栈里最后“压入”的东西将是以后第一个“弹出”的。和其他所有Java 集合一样,我们压入和弹出的都是“对象”,所以必须对自己弹出的东西进行“造型”。
一种很少见的做法是拒绝使用Vector 作为一个Stack 的基本构成元素,而是从Vector里“继承”一个Stack。这样一来,它就拥有了一个Vector 的所有特征及行为,另外加上一些额外的Stack 行为。很难判断出设计者到底是明确想这样做,还是属于一种固有的设计。
下面是一个简单的堆栈示例,它能读入数组的每一行,同时将其作为字串压入堆栈。
import java.util.*;
public class Stacks {
static String[] months = { "January", "February", "March", "April", "May",
"June", "July", "August", "September", "October", "November",
"December" };
public static void main(String[] args) {
Stack stk = new Stack();
for (int i = 0; i < months.length; i++)
stk.push(months[i] + " ");
System.out.println("stk = " + stk);
// Treating a stack as a Vector:
stk.addElement("The last line");
System.out.println("element 5 = " + stk.elementAt(5));
System.out.println("popping elements:");
while (!stk.empty())
System.out.println(stk.pop());
}
} // /:~
输出如下:
stk = [January , February , March , April , May , June , July , August , September , October , November , December ]
element 5 = June
popping elements:
The last line
December
November
October
September
August
July
June
May
April
March
February
January
months数组的每一行都通过push()继承进入堆栈,稍后用 pop()从堆栈的顶部将其取出。要声明的一点是,
Vector操作亦可针对Stack 对象进行。这可能是由继承的特质决定的——Stack“属于”一种Vector。因
此,能对Vector 进行的操作亦可针对Stack 进行,例如 elementAt()方法。
Vector允许我们用一个数字从一系列对象中作出选择,所以它实际是将数字同对象关联起来了。但假如我们想根据其他标准选择一系列对象呢?堆栈就是这样的一个例子:它的选择标准是“最后压入堆栈的东西”。
这种“从一系列对象中选择”的概念亦可叫作一个“映射”、“字典”或者“关联数组”。从概念上讲,它看起来象一个Vector,但却不是通过数字来查找对象,而是用另一个对象来查找它们!这通常都属于一个程序中的重要进程。
在Java 中,这个概念具体反映到抽象类Dictionary身上。该类的接口是非常直观的size()告诉我们其中包含了多少元素;isEmpty()判断是否包含了元素(是则为 true);put(Object key, Object value)添加一个值(我们希望的东西),并将其同一个键关联起来(想用于搜索它的东西);get(Object key)获得与某个键对应的值;而remove(Object Key)用于从列表中删除“键-值”对。还可以使用枚举技术:keys()产生对键的一个枚举(Enumeration);而 elements()产生对所有值的一个枚举。这便是一个Dict ionary(字典)的全部。
Dictionary的实现过程并不麻烦。下面列出一种简单的方法,它使用了两个Vector,一个用于容纳键,另一个用来容纳值:
import java.util.*;
public class AssocArray extends Dictionary {
private Vector keys = new Vector();
private Vector values = new Vector();
public int size() {
return keys.size();
}
public boolean isEmpty() {
return keys.isEmpty();
}
public Object put(Object key, Object value) {
keys.addElement(key);
values.addElement(value);
return key;
}
public Object get(Object key) {
int index = keys.indexOf(key);
// indexOf() Returns -1 if key not found:
if (index == -1)
return null;
return values.elementAt(index);
}
public Object remove(Object key) {
int index = keys.indexOf(key);
if (index == -1)
return null;
keys.removeElementAt(index);
Object returnval = values.elementAt(index);
values.removeElementAt(index);
return returnval;
}
public Enumeration keys() {
return keys.elements();
}
public Enumeration elements() {
return values.elements();
}
// Test it:
public static void main(String[] args) {
AssocArray aa = new AssocArray();
for (char c = 'a'; c <= 'z'; c++)
aa.put(String.valueOf(c), String.valueOf(c).toUpperCase());
char[] ca = { 'a', 'e', 'i', 'o', 'u' };
for (int i = 0; i < ca.length; i++)
System.out.println("Uppercase: " + aa.get(String.valueOf(ca[i])));
}
}
输出如下:
Uppercase: A
Uppercase: E
Uppercase: I
Uppercase: O
Uppercase: U
在对AssocArray 的定义中,我们注意到的第一个问题是它“扩展”了字典。这意味着AssocArray 属于Dictionary的一种类型,所以可对其发出与Dictionary一样的请求。如果想生成自己的Dictionary,而且就在这里进行,那么要做的全部事情只是填充位于 Dictionary内的所有方法(而且必须覆盖所有方法,因为它们——除构建器外——都是抽象的)。
Vector key 和value 通过一个标准索引编号链接起来。也就是说,如果用“roof”的一个键以及“blue”的一个值调用 put()——假定我们准备将一个房子的各部分与它们的油漆颜色关联起来,而且AssocArray 里已有100 个元素,那么“roof”就会有 101个键元素,而“blue”有101个值元素。而且要注意一下get(),假如我们作为键传递“roof”,它就会产生与keys.index.Of()的索引编号,然后用那个索引编号生成相关的值矢量内的值。
main()中进行的测试是非常简单的;它只是将小写字符转换成大写字符,这显然可用更有效的方式进行。但它向我们揭示出了AssocArray 的强大功能。
标准Java 库只包含Dictionary 的一个变种,名为 Hashtable(散列表,注释③)。Java 的散列表具有与AssocArray相同的接口(因为两者都是从 Dictionary 继承来的)。但有一个方面却反映出了差别:执行效率。若仔细想想必须为一个get()做的事情,就会发现在一个Vector里搜索键的速度要慢得多。但此时用散列表却可以加快不少速度。不必用冗长的线性搜索技术来查找一个键,而是用一个特殊的值,名为“散列码”。散列码可以获取对象中的信息,然后将其转换成那个对象“相对唯一”的整数(int)。所有对象都有一个散列码,而hashCode()是根类Object 的一个方法。Hashtable 获取对象的 hashCode(),然后用它快速查找键。这样可使性能得到大幅度提升。大家只需要知道散列表是一种快速的“字典”(Dictionary)即可,而字典是一种非常有用的工具。作为应用散列表的一个例子,可考虑用一个程序来检验Java 的Math.random()方法的随机性到底如何。在理想情况下,它应该产生一系列完美的随机分布数字。但为了验证这一点,我们需要生成数量众多的随机数字,然后计算落在不同范围内的数字多少。散列表可以极大简化这一工作,因为它能将对象同对象关联起来(此时是将 Math.random()生成的值同那些值出现的次数关联起来)。如下所示:
import java.util.*;
class Counter {
int i = 1;
public String toString() {
return Integer.toString(i);
}
}
class Statistics {
public static void main(String[] args) {
Hashtable ht = new Hashtable();
for(int i = 0; i < 10000; i++) {
// Produce a number between 0 and 20:
Integer r =
new Integer((int)(Math.random() * 20));
if(ht.containsKey(r))
((Counter)ht.get(r)).i++;
else
ht.put(r, new Counter());
}
System.out.println(ht);
}
} ///:~
输出如下:
{19=477, 18=517, 17=498, 16=519, 15=534, 14=457, 13=497, 12=485, 11=473, 10=490, 9=522, 8=482, 7=541, 6=487, 5=483, 4=541, 3=541, 2=472, 1=485, 0=499}
在main()中,每次产生一个随机数字,它都会封装到一个Integer 对象里,使句柄能够随同散列表一起使用(不可对一个集合使用基本数据类型,只能使用对象句柄)。containKey()方法检查这个键是否已经在集合里(也就是说,那个数字以前发现过吗?)若已在集合里,则 get()方法获得那个键关联的值,此时是一个Counter(计数器)对象。计数器内的值i 随后会增加 1,表明这个特定的随机数字又出现了一次。
假如键以前尚未发现过,那么方法 put()仍然会在散列表内置入一个新的“键-值”对。在创建之初,Counter会自己的变量i自动初始化为1,它标志着该随机数字的第一次出现。 为显示散列表,只需把它简单地打印出来即可。Hashtable toString()方法能遍历所有键-值对,并为每一
对都调用toString()。Integer toString()是事先定义好的,可看到计数器使用的toString。
大家或许会对Counter 类是否必要感到疑惑,它看起来似乎根本没有封装类Integer 的功能。为什么不用int或Integer呢?事实上,由于所有集合能容纳的仅有对象句柄,所以根本不可以使用整数。学过集合后,封装类的概念对大家来说就可能更容易理解了,因为不可以将任何基本数据类型置入集合里。然而,我们对Java 封装器能做的唯一事情就是将其初始化成一个特定的值,然后读取那个值。也就是说,一旦封装器对象已经创建,就没有办法改变一个值。这使得Integer 封装器对解决我们的问题毫无意义,所以不得不创建一个新类,用它来满足自己的要求。
在前面的例子里,我们用一个标准库的类(Integer)作为Hashtable 的一个键使用。作为一个键,它能很好地工作,因为它已经具备正确运行的所有条件。但在使用散列表的时候,一旦我们创建自己的类作为键使用,就会遇到一个很常见的问题。例如,假设一套天气预报系统将Groundhog(土拔鼠)对象匹配成Prediction(预报)。这看起来非常直观:我们创建两个类,然后将Groundhog 作为键使用,而将Prediction作为值使用。如下所示:
import java.util.*;
class Groundhog {
int ghNumber;
Groundhog(int n) {
ghNumber = n;
}
}
class Prediction {
boolean shadow = Math.random() > 0.5;
public String toString() {
if (shadow)
return "Six more weeks of Winter!";
else
return "Early Spring!";
}
}
public class SpringDetector {
public static void main(String[] args) {
Hashtable ht = new Hashtable();
for (int i = 0; i < 10; i++)
ht.put(new Groundhog(i), new Prediction());
System.out.println("ht = " + ht + "\n");
System.out.println("Looking up prediction for groundhog #3:");
Groundhog gh = new Groundhog(3);
if (ht.containsKey(gh))
System.out.println((Prediction) ht.get(gh));
}
} // /:~
输出:
ht = {Groundhog@146528f7=Six more weeks of Winter!, Groundhog@5e3775b3=Six more weeks of Winter!, Groundhog@21c2d173=Early Spring!, Groundhog@a0e4e812=Six more weeks of Winter!, Groundhog@65e3daca=Six more weeks of Winter!, Groundhog@66e89e4f=Six more weeks of Winter!, Groundhog@26ae1a92=Early Spring!, Groundhog@f8b9be05=Six more weeks of Winter!, Groundhog@5348f3b2=Six more weeks of Winter!, Groundhog@c2619ece=Six more weeks of Winter!}
Looking up prediction for groundhog #3:
每个Groundhog 都具有一个标识号码,所以在散列表中查找一个Prediction,只需指示它“告诉我与Groundhog 号码3相关的 Prediction”。Prediction 类包含了一个布尔值,用Math.random()进行初始化,以及一个toString()为我们解释结果。在main()中,用Groundhog 以及与它们相关的 Prediction 填充一个散列表。散列表被打印出来,以便我们看到它们确实已被填充。随后,用标识号码为 3的一个 Groundhog 查找与Groundhog #3对应的预报。
看起来似乎非常简单,但实际是不可行的。问题在于Groundhog 是从通用的 Object 根类继承的(若当初未指定基础类,则所有类最终都是从Object 继承的)。事实上是用Object 的hashCode()方法生成每个对象的散列码,而且默认情况下只使用它的对象的地址。所以,Groundhog(3)的第一个实例并不会产生与Groundhog(3)第二个实例相等的散列码,而我们用第二个实例进行检索。或许认为此时要做的全部事情就是正确地覆盖 hashCode()。但这样做依然行不能,除非再做另一件事情:覆盖也属于Object一部分的equals()。当散列表试图判断我们的键是否等于表内的某个键时,就会用到这个方法。同样地,默认的Object.equals()只是简单地比较对象地址,所以一个Groundhog(3)并不等于另一个Groundhog(3)。
因此,为了在散列表中将自己的类作为键使用,必须同时覆盖 hashCode()和equals(),就象下面展示的那样:
import java.util.*;
class Groundhog2 {
int ghNumber;
Groundhog2(int n) { ghNumber = n; }
public int hashCode() { return ghNumber; }
public boolean equals(Object o) {
return (o instanceof Groundhog2)
&& (ghNumber == ((Groundhog2)o).ghNumber);
}
}
public class SpringDetector2 {
public static void main(String[] args) {
Hashtable ht = new Hashtable();
for(int i = 0; i < 10; i++)
ht.put(new Groundhog2(i),new Prediction());
System.out.println("ht = " + ht + "\n");
System.out.println(
"Looking up prediction for groundhog #3:");
Groundhog2 gh = new Groundhog2(3);
if(ht.containsKey(gh))
System.out.println((Prediction)ht.get(gh));
}
} ///:~
输出如下:
ht = {Groundhog2@9=Early Spring!, Groundhog2@8=Early Spring!, Groundhog2@7=Six more weeks of Winter!, Groundhog2@6=Early Spring!, Groundhog2@5=Six more weeks of Winter!, Groundhog2@4=Early Spring!, Groundhog2@3=Six more weeks of Winter!, Groundhog2@2=Early Spring!, Groundhog2@1=Early Spring!, Groundhog2@0=Early Spring!}
Looking up prediction for groundhog #3:
Six more weeks of Winter!
使用了来自前一个例子的Prediction,所以SpringDetector.java 必须首先编译,否则就会在
试图编译SpringDetector2.java 时得到一个编译期错误。
Groundhog2.hashCode()将土拔鼠号码作为一个标识符返回(在这个例子中,程序员需要保证没有两个土拔鼠用同样的ID 号码并存)。为了返回一个独一无二的标识符,并不需要hashCode(),equals()方法必须能够严格判断两个对象是否相等。
equals()方法要进行两种检查:检查对象是否为null;若不为null,则继续检查是否为Groundhog2 的一个实例(要用到instanceof 关键字)。即使为了继续执行equals(),它也应该是一个Groundhog2。正如大家看到的那样,这种比较建立在实际ghNumber 的基础上。这一次一旦我们运行程序,就会看到它终于产生了正确的输出(许多Java 库的类都覆盖了hashcode()和equals()方法,以便与自己提供的内容适应)。
在第一个例子中,我们使用了一个名为 Properties(属性)的 Hashtable 类型。在那个例子中,下述程序行:
Properties p = System.getProperties();
p.list(System.out);
调用了一个名为getProperties()的static 方法,用于获得一个特殊的Properties对象,对系统的某些特征进行描述。list()属于 Properties 的一个方法,可将内容发给我们选择的任何流式输出。也有一个 save()方法,可用它将属性列表写入一个文件,以便日后用load()方法读取。
尽管Properties 类是从Hashtable 继承的,但它也包含了一个散列表,用于容纳“默认”属性的列表。所以假如没有在主列表里找到一个属性,就会自动搜索默认属性。
Properties类亦可在我们的程序中使用。在Java 库的用户文档中,往往可以找到更多、更详细的说明。
现在可以开始演示Enumeration(枚举)的真正威力:将穿越一个序列的操作与那个序列的基础结构分隔开。在下面的例子里,PrintData 类用一个Enumeration在一个序列中移动,并为每个对象都调用toString()方法。此时创建了两个不同类型的集合:一个Vector 和一个 Hashtable。并且在它们里面分别填充Mouse 和Hamster对象(早些时候已定义了这些类;注意必须先编译HamsterMaze.java 和WorksAnyway.java,否则下面的程序不能编译)。由于Enumeration隐藏了基层集合的结构,所以PrintData 不知道或者不关心Enumeration 来自于什么类型的集合:
import java.util.*;
class PrintData {
static void print(Enumeration e) {
while (e.hasMoreElements())
System.out.println(e.nextElement().toString());
}
}
class Enumerators2 {
public static void main(String[] args) {
Vector v = new Vector();
for (int i = 0; i < 5; i++)
v.addElement(new Mouse(i));
Hashtable h = new Hashtable();
for (int i = 0; i < 5; i++)
h.put(new Integer(i), new Hamster(i));
System.out.println("Vector");
PrintData.print(v.elements());
System.out.println("Hashtable");
PrintData.print(h.elements());
}
} // /:~
输出:
Vector
This is Mouse #0
This is Mouse #1
This is Mouse #2
This is Mouse #3
This is Mouse #4
Hashtable
This is Hamster #4
This is Hamster #3
This is Hamster #2
This is Hamster #1
This is Hamster #0
注意PrintData.print()利用了这些集合中的对象属于 Object类这一事实,所以它调用了toString()。但在解决自己的实际问题时,经常都要保证自己的 Enumeration穿越某种特定类型的集合。例如,可能要求集合中的所有元素都是一个 Shape(几何形状),并含有draw()方法。若出现这种情况,必须从Enumeration.nextElement()返回的 Object 进行下溯造型,以便产生一个Shape。
Java 1.0 和1.1 库都缺少的一样东西是算术运算,甚至没有最简单的排序运算方法。因此,我们最好创建一个Vector,利用经典的Quicksort(快速排序)方法对其自身进行排序。
编写通用的排序代码时,面临的一个问题是必须根据对象的实际类型来执行比较运算,从而实现正确的排序。当然,一个办法是为每种不同的类型都写一个不同的排序方法。然而,应认识到假若这样做,以后增加新类型时便不易实现代码的重复利用。
程序设计一个主要的目标就是“将发生变化的东西同保持不变的东西分隔开”。在这里,保持不变的代码是通用的排序算法,而每次使用时都要变化的是对象的实际比较方法。因此,我们不可将比较代码“硬编码”到多个不同的排序例程内,而是采用“回调”技术。利用回调,经常发生变化的那部分代码会封装到它自己的类内,而总是保持相同的代码则“回调”发生变化的代码。这样一来,不同的对象就可以表达不同的比较方式,同时向它们传递相同的排序代码。
下面这个“接口”(Interface)展示了如何比较两个对象,它将那些“要发生变化的东西”封装在内:
interface Compare {
boolean lessThan(Object lhs, Object rhs);
boolean lessThanOrEqual(Object lhs, Object rhs);
} ///:~
对这两种方法来说,lhs代表本次比较中的“左手”对象,而rhs代表“右手”对象。
可创建Vector 的一个子类,通过 Compare实现“快速排序”。
import java.util.*;
public class SortVector extends Vector {
private Compare compare; // To hold the callback
public SortVector(Compare comp) {
compare = comp;
}
public void sort() {
quickSort(0, size() - 1);
}
private void quickSort(int left, int right) {
if (right > left) {
Object o1 = elementAt(right);
int i = left - 1;
int j = right;
while (true) {
while (compare.lessThan(elementAt(++i), o1))
;
while (j > 0)
if (compare.lessThanOrEqual(elementAt(--j), o1))
break; // out of while
if (i >= j)
break;
swap(i, j);
}
swap(i, right);
quickSort(left, i - 1);
quickSort(i + 1, right);
}
}
private void swap(int loc1, int loc2) {
Object tmp = elementAt(loc1);
setElementAt(elementAt(loc2), loc1);
setElementAt(tmp, loc2);
}
} // /:~
由于quickSort()方法“往回调用”了Compare 中的方法。从中亦可理解这种技术如何生成通用的、可重复利用(再生)的代码。 为使用SortVector,必须创建一个类,令其为我们准备排序的对象实现Compare。此时内部类并不显得特别重要,但对于代码的组织却是有益的。下面是针对 String对象的一个例子:
import java.util.*;
public class StringSortTest {
static class StringCompare implements Compare {
public boolean lessThan(Object l, Object r) {
return ((String) l).toLowerCase().compareTo(
((String) r).toLowerCase()) < 0;
}
public boolean lessThanOrEqual(Object l, Object r) {
return ((String) l).toLowerCase().compareTo(
((String) r).toLowerCase()) <= 0;
}
}
public static void main(String[] args) {
SortVector sv = new SortVector(new StringCompare());
sv.addElement("d");
sv.addElement("A");
sv.addElement("C");
sv.addElement("c");
sv.addElement("b");
sv.addElement("B");
sv.addElement("D");
sv.addElement("a");
sv.sort();
Enumeration e = sv.elements();
while (e.hasMoreElements())
System.out.println(e.nextElement());
}
} // /:~
输出如下:
A
a
b
B
c
C
d
D
内部类是“静态”(Static)的,因为它毋需连接一个外部类即可工作。 大家可以看到,一旦设置好框架,就可以非常方便地重复使用象这样的一个设计——只需简单地写一个类,
将“需要发生变化”的东西封装进去,然后将一个对象传给SortVector 即可。
比较时将字串强制为小写形式,所以大写A 会排列于小写a的旁边,而不会移动一个完全不同的地方。然而,该例也显示了这种方法的一个不足,因为上述测试代码按照出现顺序排列同一个字母的大写和小写形式:A a b B c C d D。但这通常不是一个大问题,因为经常处理的都是更长的字串,所以上述效果不会显露出来(Java 1.2 的集合提供了排序功能,已解决了这个问题)。
继承(extends)在这儿用于创建一种新类型的Vector——也就是说,SortVector 属于一种Vector,并带有一些附加的功能。继承在这里可发挥很大的作用,但了带来了问题。它使一些方法具有了final 属性(已在第7 章讲述),所以不能覆盖它们。如果想创建一个排好序的 Vector,令其只接收和生成String对象,就会遇到麻烦。因为addElement()和elementAt()都具有 final属性,而且它们都是我们必须覆盖的方法,否则便无法实现只能接收和产生String 对象。
但在另一方面,请考虑采用“合成”方法:将一个对象置入一个新类的内部。此时,不是改写上述代码来达到这个目的,而是在新类里简单地使用一个SortVector。在这种情况下,用于实现Compare接口的内部类就可以“匿名”地创建。如下所示:
import java.util.*;
public class StrSortVector {
private SortVector v = new SortVector(
// Anonymous inner class:
new Compare() {
public boolean lessThan(Object l, Object r) {
return ((String) l).toLowerCase().compareTo(
((String) r).toLowerCase()) < 0;
}
public boolean lessThanOrEqual(Object l, Object r) {
return ((String) l).toLowerCase().compareTo(
((String) r).toLowerCase()) <= 0;
}
});
private boolean sorted = false;
public void addElement(String s) {
v.addElement(s);
sorted = false;
}
public String elementAt(int index) {
if (!sorted) {
v.sort();
sorted = true;
}
return (String) v.elementAt(index);
}
public Enumeration elements() {
if (!sorted) {
v.sort();
sorted = true;
}
return v.elements();
}
// Test it:
public static void main(String[] args) {
StrSortVector sv = new StrSortVector();
sv.addElement("d");
sv.addElement("A");
sv.addElement("C");
sv.addElement("c");
sv.addElement("b");
sv.addElement("B");
sv.addElement("D");
sv.addElement("a");
Enumeration e = sv.elements();
while (e.hasMoreElements())
System.out.println(e.nextElement());
}
} // /:~
输出如下:
A
a
b
B
c
C
d
D
这样便可快速再生来自 SortVector的代码,从而获得希望的功能。然而,并不是来自 SortVector 和Vector的所有public 方法都能在 StrSortVector 中出现。若按这种形式再生代码,可在新类里为包含类内的每一个方法都生成一个定义。当然,也可以在刚开始时只添加少数几个,以后根据需要再添加更多的。新类的设计最终会稳定下来。
这种方法的好处在于它仍然只接纳 String对象,也只产生String 对象。而且相应的检查是在编译期间进行的,而非在运行期。当然,只有addElement()和elementAt()才具备这一特性;elements()仍然会产生一个Enumeration(枚举),它在编译期的类型是未定的。当然,对 Enumeration 以及在 StrSortVector中的类型检查会照旧进行;如果真的有什么错误,运行期间会简单地产生一个违例。事实上,我们在编译或运行期间能保证一切都正确无误吗?(也就是说,“代码测试时也许不能保证”,以及“该程序的用户有可能做一些未经我们测试的事情”)。尽管存在其他选择和争论,使用继承都要容易得多,只是在造型时让人深感不便。同样地,一旦为Java 加入参数化类型,就有望解决这个问题。
在这个类中可以看到有一个名为“sorted”的标志。每次调用addElement()时,都可对 Vector 进行排序,而且将其连续保持在一个排好序的状态。但在开始读取之前,人们总是向一个Vector 添加大量元素。所以与其在每个addElement()后排序,不如一直等到有人想读取Vector,再对其进行排序。后者的效率要高得多。这种除非绝对必要,否则就不采取行动的方法叫作“懒惰求值”(还有一种类似的技术叫作“懒惰初始化”——除非真的需要一个字段值,否则不进行初始化)。
标准 Java 库提供了一些特别有用的集合,但距完整意义的集合尚远。除此之外,象排序这样的算法根本没有提供支持。C++出色的一个地方就是它的库,特别是“标准模板库”(STL)提供了一套相当完整的集合,以及许多象排序和检索这样的算法,可以非常方便地对那些集合进行操作。有感这一现状,并以这个模型为基础,ObjectSpace 公司设计了 Java 版本的“通用集合库”(从前叫作“Java通用库”,即JGL;但JGL这个缩写形式侵犯了Sun公司的版权——尽管本书仍然沿用这个简称)。这个库尽可能遵照STL的设计(照顾到两种语言间的差异)。JGL实现了许多功能,可满足对一个集合库的大多数常规需求,它与C++的模板机制非常相似。JGL包括相互链接起来的列表、设置、队列、映射、堆栈、序列以及反复器,它们的功能比Enumeration(枚举)强多了。同时提供了一套完整的算法,如检索和排序等。在某些方面,ObjectSpace的设计也显得比Sun 的库设计方案“智能”一些。举个例子来说,JGL 集合中的方法不会进入final 状态,所以很容易继承和改写那些方法。
JGL已包括到一些厂商发行的Java 套件中,而且ObjectSpace公司自己也允许所有用户免费使用 JGL,包括商业性的使用。详细情况和软件下载可访问http://www.ObjectSpace.com。与JGL 配套提供的联机文档做得非常好,可作为自己的一个绝佳起点使用。
集合类属于最强大的一种工具,特别适合在原创编程中使用。大家可能已感觉到对 Java 1.1 提供的集合多少有点儿失望。因此,看到Java 1.2 对集合重新引起了正确的注意后,确实令人非常愉快。这个版本的集合也得到了完全的重新设计(由Sun 公司的Joshua B loch)。新设计的集合是Java 1.2 中两项最主要的特性之一(另一项是 Swing库),因为它们极大方便了我们的编程,也使Java 变成一种更成熟的编程系统。
有些设计使得元素间的结合变得更紧密,也更容易让人理解。例如,许多名字都变得更短、更明确了,而且更易使用;类型同样如此。有些名字进行了修改,更接近于通俗:我感觉特别好的一个是用“反复器”(Inerator)代替了“枚举”(Enumeration)。
此次重新设计也加强了集合库的功能。现在新增的行为包括链接列表、队列以及撤消组队(即“双终点队列”)。
集合库的设计是相当困难的(会遇到大量库设计问题)。在C++中,STL用多个不同的类来覆盖基础。这种做法比起STL 以前是个很大的进步,那时根本没做这方面的考虑。但仍然没有很好地转换到Java 里面。结果就是一大堆特别容易混淆的类。在另一个极端,我曾发现一个集合库由单个类构成:colleciton,它同时作为Vector和Hashtable 使用。新集合库的设计者则希望达到一种新的平衡:实现人们希望从一个成熟集合库上获得的完整功能,同时又要比STL 和其他类似的集合库更易学习和使用。这样得到的结果在某些场合显得有
些古怪。但和早期Java 库的一些决策不同,这些古怪之处并非偶然出现的,而是以复杂性作为代价,在进行仔细权衡之后得到的结果。这样做也许会延长人们掌握一些库概念的时间,但很快就会发现自己很乐于使用那些新工具,而且变得越来越离不了它。
新的集合库考虑到了“容纳自己对象”的问题,并将其分割成两个明确的概念:
(1) 集合(Collection):一组单独的元素,通常应用了某种规则。在这里,一个List(列表)必须按特定的顺序容纳元素,而一个Set(集)不可包含任何重复的元素。相反,“包”(Bag)的概念未在新的集合库中实现,因为“列表”已提供了类似的功能。
(2) 映射(Map):一系列“键-值”对(这已在散列表身上得到了充分的体现)。从表面看,这似乎应该成为一个“键-值”对的“集合”,但假若试图按那种方式实现它,就会发现实现过程相当笨拙。这进一步证明了应该分离成单独的概念。另一方面,可以方便地查看 Map的某个部分。只需创建一个集合,然后用它表示那一部分即可。这样一来,Map 就可以返回自己键的一个Set、一个包含自己值的List 或者包含自己“键-值”对的一个List。和数组相似,Map可方便扩充到多个“维”,毋需涉及任何新概念。只需简单地在一个Map 里包含其他Map(后者又可以包含更多的Map,以此类推)。
Collection和Map可通过多种形式实现,具体由编程要求决定。下面列出的是一个帮助大家理解的新集合示意图:
这张图刚开始的时候可能让人有点儿摸不着头脑,大家会真正理解它实际只有三个集合组件:Map,List 和Set。而且每个组件实际只有两、三种实现方式(示意图没有包括以后会加入的TreeSet),而且通常都只有一种特别好的方式。只要看出了这一点,集合就不会再令人生畏。
虚线框代表“接口”,点线框代表“抽象”类,而实线框代表普通(实际)类。点线箭头表示一个特定的类准备实现一个接口(在抽象类的情况下,则是“部分”实现一个接口)。双线箭头表示一个类可生成箭头指向的那个类的对象。例如,任何集合都可以生成一个反复器(Iterator),而一个列表可以生成一个ListIterator(以及原始的反复器,因为列表是从集合继承的)。
致力于容纳对象的接口是Collection,List,Set 和Map。在传统情况下,我们需要写大量代码才能同这些接口打交道。而且为了指定自己想使用的准确类型,必须在创建之初进行设置。所以可能创建下面这样的一个List:
List x = new LinkedList();
当然,也可以决定将x 作为一个LinkedList 使用(而不是一个普通的List),并用x负载准确的类型信息。使用接口的好处就是一旦决定改变自己的实施细节,要做的全部事情就是在创建的时候改变它,就象下面这样:
List x = new ArrayList();
其余代码可以保持原封不动。
在类的分级结构中,可看到大量以“Abstract”(抽象)开头的类,这刚开始可能会使人感觉迷惑。它们实际上是一些工具,用于“部分”实现一个特定的接口。举个例子来说,假如想生成自己的Set,就不是从 Set接口开始,然后自行实现所有方法。相反,我们可以从AbstractSet继承,只需极少的工作即可得到自己的新类。尽管如此,新集合库仍然包含了足够的功能,可满足我们的几乎所有需求。所以考虑到我们的目的,可忽略所有以“Abstract”开头的类。
因此,在观看这张示意图时,真正需要关心的只有位于最顶部的“接口”以及普通(实际)类——均用实线方框包围。通常需要生成实际类的一个对象,将其上溯造型为对应的接口。以后即可在代码的任何地方使用那个接口。下面是一个简单的例子,它用String 对象填充一个集合,然后打印出集合内的每一个元素:
import java.util.*;
public class SimpleCollection {
public static void main(String[] args) {
Collection c = new ArrayList();
for (int i = 0; i < 10; i++)
c.add(Integer.toString(i));
Iterator it = c.iterator();
while (it.hasNext())
System.out.println(it.next());
}
} // /:~
输出如下:
0
1
2
3
4
5
6
7
8
9
新集合库的所有代码示例都置于子目录newcollections 下,这样便可提醒自己这些工作只对于Java 1.2 有效。这样一来,我们必须用下述代码来调用程序:
java newcollections.SimpleCollection
采用的语法与其他程序是差不多的。
大家可以看到新集合属于java.util 库的一部分,所以在使用时不需要再添加任何额外的import语句。 main()的第一行创建了一个ArrayList 对象,然后将其上溯造型成为一个集合。由于这个例子只使用了Collection方法,所以从 Collection 继承的一个类的任何对象都可以正常工作。但ArrayList 是一个典型的Collection,它代替了 Vector 的位置。
显然,add()方法的作用是将一个新元素置入集合里。然而,用户文档谨慎地指出 add()“保证这个集合包含了指定的元素”。这一点是为Set 作铺垫的,后者只有在元素不存在的前提下才会真的加入那个元素。对于ArrayList 以及其他任何形式的List,add()肯定意味着“直接加入”。 利用iterator()方法,所有集合都能生成一个“反复器”(Iterator)。反复器其实就象一个“枚举”(Enumeration),是后者的一个替代物,只是:
(1) 它采用了一个历史上默认、而且早在OOP 中得到广泛采纳的名字(反复器)。
(2) 采用了比Enumeration 更短的名字:hasNext()代替了 hasMoreElement(),而next()代替了nextElement()。
(3) 添加了一个名为remove()的新方法,可删除由Iterator 生成的上一个元素。所以每次调用next()的时候,只需调用remove()一次。
在SimpleCollection.java 中,大家可看到创建了一个反复器,并用它在集合里遍历,打印出每个元素。
表格总结了用一个集合能做的所有事情(亦可对 Set和List 做同样的事情,尽管List 还提供了一些额外的功能)。Map不是从Collection 继承的,所以要单独对待。
boolean add(Object) *保证集合内包含了自变量。如果它没有添加自变量,就返回 false(假)
boolean addAll(Collection) *添加自变量内的所有元素。如果没有添加元素,则返回 true(真)
void clear() *删除集合内的所有元素
boolean contains(Object) 若集合包含自变量,就返回“真”
boolean containsAll(Collection) 若集合包含了自变量内的所有元素,就返回“真”
boolean isEmpty() 若集合内没有元素,就返回“真”
Iterator iterator() 返回一个反复器,以用它遍历集合的各元素
boolean remove(Object) *如自变量在集合里,就删除那个元素的一个实例。如果已进行了删除,就返回“真”
boolean removeAll(Collection) *删除自变量里的所有元素。如果已进行了任何删除,就返回“真”
boolean retainAll(Collection) *只保留包含在一个自变量里的元素(一个理论的“交集”)。如果已进行了任何改变,就返回“真”
int size() 返回集合内的元素数量
Object[] toArray() 返回包含了集合内所有元素的一个数组
*这是一个“可选的”方法,有的集合可能并未实现它。若确实如此,该方法就会遇到一个UnsupportedOperatiionException,即一个“操作不支持”违例。
下面这个例子向大家演示了所有方法。同样地,它们只对从集合继承的东西有效,一个ArrayList 作为一种“不常用的分母”使用:
import java.util.*;
public class Collection1 {
// Fill with 'size' elements, start
public static Collection fill(Collection c, int start, int size) {
for (int i = start; i < start + size; i++)
c.add(Integer.toString(i));
return c;
}
// Default to a "start" of 0:
public static Collection fill(Collection c, int size) {
return fill(c, 0, size);
}
// Default to 10 elements:
public static Collection fill(Collection c) {
return fill(c, 0, 10);
}
// Create & upcast to Collection:
public static Collection newCollection() {
return fill(new ArrayList());
// ArrayList is used for simplicity, but it's
// only seen as a generic Collection
// everywhere else in the program.
}
// Fill a Collection with a range of values:
public static Collection newCollection(int start, int size) {
return fill(new ArrayList(), start, size);
}
// Moving through a List with an iterator:
public static void print(Collection c) {
for (Iterator x = c.iterator(); x.hasNext();)
System.out.print(x.next() + " ");
System.out.println();
}
public static void main(String[] args) {
Collection c = newCollection();
c.add("ten");
c.add("eleven");
print(c);
// Make an array from the List:
Object[] array = c.toArray();
// Make a String array from the List:
String[] str = (String[]) c.toArray(new String[1]);
// Find max and min elements; this means
// different things depending on the way
// the Comparable interface is implemented:
System.out.println("Collections.max(c) = " + Collections.max(c));
System.out.println("Collections.min(c) = " + Collections.min(c));
// Add a Collection to another Collection
c.addAll(newCollection());
print(c);
c.remove("3"); // Removes the first one
print(c);
c.remove("3"); // Removes the second one
print(c);
// Remove all components that are in the
// argument collection:
c.removeAll(newCollection());
print(c);
c.addAll(newCollection());
print(c);
// Is an element in this Collection?
System.out.println("c.contains(\"4\") = " + c.contains("4"));
// Is a Collection in this Collection?
System.out.println("c.containsAll(newCollection()) = "
+ c.containsAll(newCollection()));
Collection c2 = newCollection(5, 3);
// Keep all the elements that are in both
// c and c2 (an intersection of sets):
c.retainAll(c2);
print(c);
// Throw away all the elements in c that
// also appear in c2:
c.removeAll(c2);
System.out.println("c.isEmpty() = " + c.isEmpty());
c = newCollection();
print(c);
c.clear(); // Remove all elements
System.out.println("after c.clear():");
print(c);
}
} // /:~
输出如下:
0 1 2 3 4 5 6 7 8 9 ten eleven
Collections.max(c) = ten
Collections.min(c) = 0
0 1 2 3 4 5 6 7 8 9 ten eleven 0 1 2 3 4 5 6 7 8 9
0 1 2 4 5 6 7 8 9 ten eleven 0 1 2 3 4 5 6 7 8 9
0 1 2 4 5 6 7 8 9 ten eleven 0 1 2 4 5 6 7 8 9
ten eleven
ten eleven 0 1 2 3 4 5 6 7 8 9
c.contains("4") = true
c.containsAll(newCollection()) = true
5 6 7
c.isEmpty() = true
0 1 2 3 4 5 6 7 8 9
after c.clear():
通过第一个方法,我们可用测试数据填充任何集合。在当前这种情况下,只是将int 转换成String。第二个方法将在本章其余的部分经常采用。
newCollection()的两个版本都创建了ArrayList,用于包含不同的数据集,并将它们作为集合对象返回。所以很明显,除了Collection 接口之外,不会再用到其他什么。
print()方法也会在本节经常用到。由于它用一个反复器(Iterator)在一个集合内遍历,而任何集合都可以产生这样的一个反复器,所以它适用于List 和Set,也适用于由一个Map 生成的Collection。
main()用简单的手段显示出了集合内的所有方法。
在后续我们将比较List,Set和Map的不同实现方案,同时指出在各种情况下哪一种方案应成为首选(带有星号的那个)。大家会发现这里并未包括一些传统的类,如Vector,Stack 以及Hashtable 等。不管在什么情况下,新集合内都有自己首选的类。