友情提示:如果本网页打开太慢或显示不完整,请尝试鼠标右键“刷新”本网页!阅读过程发现任何错误请告诉我们,谢谢!! 报告错误
一世书城 返回本书目录 我的书架 我的书签 TXT全本下载 进入书吧 加入书签

Java编程思想第4版[中文版](PDF格式)-第124章

按键盘上方向键 ← 或 → 可快速上下翻页,按键盘上的 Enter 键可回到本书目录页,按键盘上方向键 ↑ 可回到本页顶部!
————未阅读完?加入书签已便下次继续阅读!






请观察下述代码:  

  

//: Stringer。java  

  

public class Stringer {  

  static String upcase(String s) {  

    return s。toUpperCase();  

  }  

  public static void main(String'' args) {  

    String q = new String(〃howdy〃);  

    System。out。println(q); // howdy  

    String qq = upcase(q);  

    System。out。println(qq); // HOWDY  

    System。out。println(q); // howdy  

  }  

} ///:~  

  

q 传递进入 upcase()时,它实际是q 的句柄的一个副本。该句柄连接的对象实际只在一个统一的物理位置 

处。句柄四处传递的时候,它的句柄会得到复制。  

若观察对upcase() 的定义,会发现传递进入的句柄有一个名字 s,而且该名字只有在upcase()执行期间才会 

存在。upcase()完成后,本地句柄 s 便会消失,而 upcase()返回结果——还是原来那个字串,只是所有字符 

都变成了大写。当然,它返回的实际是结果的一个句柄。但它返回的句柄最终是为一个新对象的,同时原来 

的q 并未发生变化。所有这些是如何发生的呢?  

  

1。 隐式常数  

若使用下述语句:  

String s = 〃asdf〃;  

String x = Stringer。upcase(s);  

那么真的希望upcase()方法改变自变量或者参数吗?我们通常是不愿意的,因为作为提供给方法的一种信 

息,自变量一般是拿给代码的读者看的,而不是让他们修改。这是一个相当重要的保证,因为它使代码更易 

编写和理解。  

为了在C++中实现这一保证,需要一个特殊关键字的帮助:const。利用这个关键字,程序员可以保证一个句 

柄(C++叫“指针”或者“引用”)不会被用来修改原始的对象。但这样一来,C++程序员需要用心记住在所 

有地方都使用const。这显然易使人混淆,也不容易记住。  

  

2。 覆盖〃+〃和StringBuffer  

利用前面提到的技术,String 类的对象被设计成 “不可变”。若查阅联机文档中关于String 类的内容(本 

章稍后还要总结它),就会发现类中能够修改 String 的每个方法实际都创建和返回了一个崭新的String 对 

象,新对象里包含了修改过的信息——原来的 String 是原封未动的。因此,Java 里没有与C++的const 对应 

的特性可用来让编译器支持对象的不可变能力。若想获得这一能力,可以自行设置,就象String 那样。  

由于String 对象是不可变的,所以能够根据情况对一个特定的 String 进行多次别名处理。因为它是只读 

的,所以一个句柄不可能会改变一些会影响其他句柄的东西。因此,只读对象可以很好地解决别名问题。  

通过修改产生对象的一个崭新版本,似乎可以解决修改对象时的所有问题,就象 String 那样。但对某些操作 

来讲,这种方法的效率并不高。一个典型的例子便是为String 对象覆盖的运算符“+”。“覆盖”意味着在 

与一个特定的类使用时,它的含义已发生了变化(用于String 的“+”和“+=”是Java 中能被覆盖的唯一运 

算符,Java 不允许程序员覆盖其他任何运算符——注释④)。  

  



                                                                                 373 


…………………………………………………………Page 375……………………………………………………………

④:C++允许程序员随意覆盖运算符。由于这通常是一个复杂的过程(参见《Thinking in C++》,Prentice

Hall 于 1995 年出版),所以Java 的设计者认定它是一种“糟糕”的特性,决定不在 Java 中采用。但具有 

讽剌意味的是,运算符的覆盖在Java 中要比在C++中容易得多。  

  

针对String 对象使用时,“+”允许我们将不同的字串连接起来:  

  

String s = 〃abc〃 + foo + 〃def〃 + Integer。toString(47);  

  

可以想象出它“可能”是如何工作的:字串〃abc〃可以有一个方法append(),它新建了一个字串,其中包含 

〃abc〃以及foo 的内容;这个新字串然后再创建另一个新字串,在其中添加〃def〃;以此类推。  

这一设想是行得通的,但它要求创建大量字串对象。尽管最终的目的只是获得包含了所有内容的一个新字 

串,但中间却要用到大量字串对象,而且要不断地进行垃圾收集。我怀疑 Java 的设计者是否先试过种方法 

 (这是软件开发的一个教训——除非自己试试代码,并让某些东西运行起来,否则不可能真正了解系统)。 

我还怀疑他们是否早就发现这样做获得的性能是不能接受的。  

解决的方法是象前面介绍的那样制作一个可变的同志类。对字串来说,这个同志类叫作StringBuffer,编译 

器可以自动创建一个StringBuffer,以便计算特定的表达式,特别是面向String 对象应用覆盖过的运算符+ 

和+=时。下面这个例子可以解决这个问题:  

  

//: ImmutableStrings。java  

// Demonstrating StringBuffer  

  

public class ImmutableStrings {  

  public static void main(String'' args) {  

    String foo = 〃foo〃;  

    String s = 〃abc〃 + foo +   

      〃def〃 + Integer。toString(47);  

    System。out。println(s);  

    // The 〃equivalent〃 using StringBuffer:  

    StringBuffer sb =   

      new StringBuffer(〃abc〃); // Creates String!  

    sb。append(foo);  

    sb。append(〃def〃); // Creates String!  

    sb。append(Integer。toString(47));  

    System。out。println(sb);  

  }  

} ///:~  

  

创建字串 s 时,编译器做的工作大致等价于后面使用 sb 的代码——创建一个StringBuffer,并用 append() 

将新字符直接加入 StringBuffer 对象(而不是每次都产生新对象)。尽管这样做更有效,但不值得每次都创 

建象〃abc〃和〃def〃这样的引号字串,编译器会把它们都转换成 String 对象。所以尽管StringBuffer 提供了 

更高的效率,但会产生比我们希望的多得多的对象。  



12。4。4 String 和 StringBuffer 类  



这里总结一下同时适用于String 和StringBuffer 的方法,以便对它们相互间的沟通方式有一个印象。这些 

表格并未把每个单独的方法都包括进去,而是包含了与本次讨论有重要关系的方法。那些已被覆盖的方法用 

单独一行总结。  

首先总结String 类的各种方法:  

  

方法  自变量,覆盖 用途  

  

构建器 已被覆盖:默认,String,StringBuffer,char 数组,byte 数组 创建String 对象  

length() 无 String 中的字符数量  



                                                                                     374 


…………………………………………………………Page 376……………………………………………………………

charAt() int Index 位于String 内某个位置的char  

getChars(),getBytes 开始复制的起点和终点,要向其中复制内容的数组,对目标数组的一个索引 将 char 

或byte 复制到外部数组内部  

toCharArray() 无 产生一个char'',其中包含了String 内部的字符  

equals(),equalsIgnoreCase() 用于对比的一个 String 对两个字串的内容进行等价性检查  

pareTo() 用于对比的一个String 结果为负、零或正,具体取决于String 和自变量的字典顺序。注意大 

写和小写不是相等的!  

regionMatches() 这个String 以及其他String 的位置偏移,以及要比较的区域长度。覆盖加入了“忽略大 

小写”的特性 一个布尔结果,指出要对比的区域是否相同  

startsWith() 可能以它开头的String。覆盖在自变量里加入了偏移 一个布尔结果,指出String 是否以那 

个自变量开头  

endsWith() 可能是这个 String 后缀的一个 String 一个布尔结果,指出自变量是不是一个后缀  

indexOf();lastIndexOf() 已覆盖:char,char 和起始索引,String,String 和起始索引 若自变量未在这 

个String 里找到,则返回…1;否则返回自变量开始处的位置索引。lastIndexOf()可从终点开始回溯搜索  

substring() 已覆盖:起始索引,起始索引和结束索引 返回一个新的String 对象,其中包含了指定的字符 

子集  

concat() 想连结的String 返回一个新 String 对象,其中包含了原始 String 的字符,并在后面加上由自变 

量提供的字符  

relpace() 要查找的老字符,要用它替换的新字符 返回一个新 String 对象,其中已完成了替换工作。若没 

有找到相符的搜索项,就沿用老字串  

toLowerCase();toUpperCase() 无 返回一个新 String 对象,其中所有字符的大小写形式都进行了统一。若 

不必修改,则沿用老字串  

trim() 无 返回一个新的String 对象,头尾空白均已删除。若毋需改动,则沿用老字串  

valueOf() 已覆盖:object,char'',char''和偏移以及计数,boolean,char,int,long,float,double  

返回一个String,其中包含自变量的一个字符表现形式  

Intern() 无 为每个独一无二的字符顺序都产生一个(而且只有一个)String 句柄  

  

可以看到,一旦有必要改变原来的内容,每个 String 方法都小心地返回了一个新的 String 对象。另外要注 

意的一个问题是,若内容不需要改变,则方法只返回指向原来那个String 的一个句柄。这样做可以节省存储 

空间和系统开销。  

下面列出有关StringBuffer (字串缓冲)类的方法:  

  

方法  自变量,覆盖 用途  

  

构建器 已覆盖:默认,要创建的缓冲区长度,要根据它创建的String 新建一个StringBuffer 对象  

toString() 无 根据这个StringBuffer 创建一个 String  

length() 无 StringBuffer 中的字符数量  

capacity() 无 返回目前分配的空间大小  

ensureCapacity() 用于表示希望容量的一个整数 使StringBuffer 容纳至少希望的空间大小  

setLength() 用于指示缓冲区内字串新长度的一个整数 缩短或扩充前一个字符串。如果是扩充,则用 null 

值填充空隙  

charAt() 表示目标元素所在位置的一个整数 返回位于缓冲区指定位置处的 char  

setCharAt() 代表目标元素位置的一个整数以及元素的一个新 char 值 修改指定位置处的值  

getChars() 复制的起点和终点,要在其中复制的数组以及目标数组的一个索引 将 char 复制到一个外部数 

组。和 
返回目录 上一页 下一页 回到顶部 0 0
未阅读完?加入书签已便下次继续阅读!
温馨提示: 温看小说的同时发表评论,说出自己的看法和其它小伙伴们分享也不错哦!发表书评还可以获得积分和经验奖励,认真写原创书评 被采纳为精评可以获得大量金币、积分和经验奖励哦!