车辆预约系统外文翻译资料

 2022-05-30 09:05

英语原文共 51 页,剩余内容已隐藏,支付完成后下载完整资料


字符串

可以证明,字符串操作是计算机程序设计中最常见的行为。

尤其是在java大展拳脚的Web系统中更是如此。在本章中,我们将深入学习在Java语言中应用最广泛的String类,并研究与之相关的类与工具。

不可变String
String对象是不可变的。查看JDK文档就会发现。String类中每一个看起来会修改String值的方法,实际上都是创建了一个全新的String对象,以及包含修改后的字符串内容。而最初的String对象则丝毫未动。

看看以下的代码:

//: strings/Immutable.java

import static net.mindview.util.Print.*;

public class Immutable {

public static String upcase(String s) { return s.toUpperCase();

}

public static void main(String[] args) { String q = 'howdy';

print(q); // howdy String qq = upcase(q); print(qq); // HOWDY print(q); // howdy

}

} /* Output: howdy

HOWDY

howdy

*///:~

当把q传给upcase()方法时,实际传递的是引用的一个拷贝。其实,每当把String对象作为方法的参数时,都会复制一份引用,而该引用所指的对象其实一直待在单一的物理位置上,从未动过。

回到upcase()的定义,传入其中的引用有了名字s。只有upcase()运行的时候,局部引用s才存在。一旦upcase()运行结束,s就消失了。当然,upcase()的返回值,其只是在最终结果的引用。这可以说明,upcase()返回的引用已经指向了一个新的对象,而原本的q则还在原地。

String的这种行为方式其实正是我们想要的,例如:

String s = 'asdf';

String x = Immutable.upcase(s);

难道你真的希望upcase()改变其参数嘛?对于一个方法而言。参数是为方法提供信息的,而不是想让方法改变自己的。在阅读这段代码时,读者自然就会有这样的感觉。这一点很重要,正是有了这种保障,才使得代码易于编写和阅读。

重载“ ”与StringBuilder

String对象是不变的,你可以给一个String对象加任意多的别名。因为String对象具有只读特性,所以指向它的任何引用都不可能改变它的值。因此,也就不会对其他的引用有什么影响。

不可变性会带来一定的效率问题。为String对象重载的“ ”操作符就是一个例子。重载的意思是,一个操作符在应用于特定的类时,被赋予了特殊的意义(用于String的“ ”与“ =”是Java中仅有的两个重载过的操作符而Java并不允许程序员重载任何操作符)。

操作符“ ”可以用来连接String:

//: strings/Concatenation.java

public class Concatenation {

public static void main(String[] args) {

String mango = 'mango';

String s = 'abc' mango 'def' 47; System.out.println(s);

}

} /* Output: abcmangodef47

*///:~

可以想象一下,这段代码可能是这样工作的:String可能有一个append()方法,它会生成一个新的String对象,以包含“abc”与mango连接后的字符串。然后,该对象再与“def”相连,生成另一个新的String对象,依此类推。

这种工作方式当然可以,但是为了生成最终的String,此方式会产生一大堆需要垃圾回收的中间对象。我猜测,Java设计师一开始就是这么做的(这也是软件设计中的一个教训:除非你用代码将系统实现,并让它动起来,否则你无法真正了解它会有什么问题),然后他们发现其功能性非常差。

想看看以上代码到底是如何工作的吗,可以用JDK自带的工具javap来反编译以上代码。命令如下:

javap -c Concatenation

这里的-c标志表示生成JVM字节码。我剔除掉了不感兴趣的部分,然后做了一点点修改,于是有了以下的字节码:

public static void main(java.lang.String[]); Code:

Stack=2, Locals=3, Args_size=1 0: ldc #2; //String mango

2: astore_1

3: new #3; //class StringBuilder

6: dup

7: invokespecial #4; //StringBuilder.'lt;initgt;':()

10: ldc #5; // String abc

12 invokevirtual #6; //StringBuilder.append:(String)

  1. aload_1
  2. invokevirtual #6; //StringBuilder.append:(String)

19 ldc #7; //String def

21 invokevirtual #6; //StringBuilder.append:(String)

24 bipush 47

26 invokevirtual #8; //StringBuilder.append:(I)

29 invokevirtual #9; //StringBuilder.toString:()

  1. astore_2
  2. getstatic #10; //Field System.out:PrintStream;
  3. aload_2
  4. invokevirtual #11; // PrintStream.println:(String)

40 return

如果你有汇编语言的经验,以上代码一定看着很眼熟,其中的dup与invokevirtural语句相当于java虚拟机上的汇编语句。即使你完全不了解汇编语言也无须担心。需要注意的重点是:编译器自动引入了java.lang.StringBuilder类。虽然在我们的源代码中并没有使用StringBuilder类,但是编译器却自作主张的使用了它,因为它更高效。

在这个例子中,编译器创建了一个StringBuilder对象,用以构造最终的String,并为每个字符串调用一次StringBuilder的append()方法,总计4次。最后调用toString()生成结果,并存为s(使用的命令为astore_2)。

现在,也许你会觉得可以随意使用String对象,反正编译器会为你自动地优化性能,可是在这之前,让我们更深入地看看编译器能为我们优化到什么程度。下面的程序采用两种方式生成一个String:方法一使用了多个String对象;方法二在代码中使用了StringBuilder。

//: strings/WhitherStringBuilder.java public class WhitherStringBuilder {

public String implicit(String[] fields) { String result = '';

for(int i = 0; i lt; fields.length; i ) result = fields[i];

return result;

}

public String explicit(String[] fields) { StringBuilder result = new StringBuilder(); for(int i = 0; i lt; fields.length; i )

result.append(fields[i]); return result.toString();

}

} ///:~

现在运行javap -c WitherStringBuilder,可以看到两个方法对应的(简化过的)字节码。首先是implicit()方法:

public java.lang.String implicit(java.lang.String[]); Code:

0: ldc #2; //String

2: astore_2

3: iconst_0

4: istore_3

5: iload_3

6: aload_1

7: arraylength

8: if_icmpge 38

11: new #3; //class StringBuilder

14: dup

15: invokespecial #4; // StringBuilder.”lt;initgt;”:()

18: aload_2

19: invokevirtual #5; // StringBuilder.append:()

22: aload_1

  1. iload_3
  2. aaload

25: invokevirtual #5; // StringBuilder.append:()

28: invokevirtual #6; // StringBuiIder.toString:()

31: astore_2

32: iinc 3, 1

35: goto 5

38: aload_2

39 areturn

注意从第8行到第35行构成了一个循环体。第8行:对堆栈中的操作数进行“大于或等于的整数比较运算”,循环结束时跳刀第38行。第35行:返回循环体的起始点(第5行)。要注意的重点是:StringBuilder实在循环之内构造的,这意味着每经过循环一次,就会创建一个新的StringBuilder对象。

下面是explicit()方法对应的字节码:

public java.lang.String explicit(java.lang.String[]); Code:

0: new #3; //class StringBuilder

3: dup

4: invokespecial #4; // StringBuilder.”lt;initgt;”:()

7: astore_2

8: iconst_0

9: istore_3

10: iload_3

11: aload_1

12: arraylength

13: if_icmpge 30

16: aload_2

17: aload_1

18: iload_3

19: aaload

20 invokevirtual #5; // StringBuilder.append:()

23 pop

24: iinc 3,1

27: goto 10

30: aload_2

31: invokevirtual #6; // StringBuiIder.toString:()

34: areturn

可以看到,不仅循环部分的代码更简短、更简单,而且它只生成了一个StringBuilder对象。显式地创建StringBUilder还允许你预先指定其大小。如果你已经知道最终字符串大概有多长,那预先指定StringBuilder的大小可以避免多次重新分配缓冲。

因此,当你为一个类编写toString()方法时,如果字符串操作比较简单,那就可以信赖编译器,它会为你合理地构造最终的字符串结果。但是,如果你在toString()方法中使用循环,那么最好自己创建一个StringBuilder对象,用它来构造最终的结果。请参考以下示例:

//: strings/UsingStringBuilder.java import java.util.*;

public class UsingStringBuilder {

public static Random rand = new Random(47); public String toString() {

StringBuilder result = new StringBuilder('['); for(int i = 0; i lt; 25; i ) {

result.append(rand.nextInt(100)); result.append(

全文共23597字,剩余内容已隐藏,支付完成后下载完整资料


资料编号:[11571],资料为PDF文档或Word文档,PDF文档可免费转换为Word

原文和译文剩余内容已隐藏,您需要先支付 30元 才能查看原文和译文全部内容!立即支付

以上是毕业论文外文翻译,课题毕业论文、任务书、文献综述、开题报告、程序设计、图纸设计等资料可联系客服协助查找。