下面的程序所要做的事情正是前一個(gè)謎題所做的事情,但是它沒(méi)有假設(shè)斜杠符號(hào)就是分隔文件名組成部分的符號(hào)。相反,該程序使用的是java.io.File.separator,它被指定為一個(gè)公共的String域,包含了平臺(tái)相關(guān)的文件名分隔符。那么,這個(gè)程序會(huì)打印出其正確的、平臺(tái)相關(guān)的類文件名嗎?
package com.javapuzzlers;
import java.io.File;
public class MeToo {
public static void main(String[] args){
System.out.println(MeToo.class.getName().
replaceAll("\\.", File.separator) + ".class");
}
}
這個(gè)程序根據(jù)底層平臺(tái)的不同會(huì)顯示兩種行為中的一種。如果文件分隔符是斜杠,就像在UNIX上一樣,那么該程序?qū)⒋蛴om/javapuzzlers/MeToo.class,這是正確的。但是,如果文件分隔符是反斜杠,就像在Windows上一樣,那么該程序?qū)⒋蛴∠裣旅孢@樣的內(nèi)容:
Exception in thread "main"
java.lang.StringIndexOutOfBoundsException: String index out of range: 1
at java.lang.String.charAt(String.java:558)
at java.util.regex.Matcher.appendReplacement(Mather.
java:696)
at java.util.regex.Matcher.replaceAll(Mather.java:806)
at java.lang.String.replaceAll(String.java:2000)
at com.javapuzzlers.MeToo.main(MeToo.java:6)
盡管這種行為是平臺(tái)相關(guān)的,但是它并非就是我們所期待的。在Windows上出了什么錯(cuò)呢?
事實(shí)證明,String.replaceAll的第二個(gè)參數(shù)不是一個(gè)普通的字符串,而是一個(gè)替代字符串(replacement string),就像在java.util.regex規(guī)范中所定義的那樣[Java-API]。在替代字符串中出現(xiàn)的反斜杠會(huì)把緊隨其后的字符進(jìn)行轉(zhuǎn)義,從而導(dǎo)致其被按字面含義而處理了。
當(dāng)你在Windows上運(yùn)行該程序時(shí),替代字符串是單獨(dú)的一個(gè)反斜杠,它是無(wú)效的。不可否認(rèn),拋出的異常應(yīng)該提供更多一些有用的信息。
那么你應(yīng)該怎樣解決此問(wèn)題呢?5.0版本提供了不是一個(gè)而是兩個(gè)新的方法來(lái)解決它。第一個(gè)方法是java.util.regex.Matcher.quoteReplacement,它將字符串轉(zhuǎn)換成相應(yīng)的替代字符串。下面展示了如何使用這個(gè)方法來(lái)訂正該程序:
System.out.println(MeToo.class.getName().replaceAll("\\.",
Matcher.quoteReplacement(File.separator)) + ".class");
引入到5.0版本中的第二個(gè)方法提供了一個(gè)更好的解決方案。該方法就是String.replace(CharSequence, CharSequence),它做的事情和String.replaceAll相同,但是它將模式和替代物都當(dāng)作字面含義的字符串處理。下面展示了如何使用這個(gè)方法來(lái)訂正該程序:
System.out.println(MeToo.class.getName().
replace(".", File.separator) + ".class");
但是如果你使用的是較早版本的Java該怎么辦?很遺憾,沒(méi)有任何捷徑能夠生成替代字符串。完全不使用正則表達(dá)式,而使用String.replace(char,char)也許要顯得更容易一些:
System.out.println(MeToo.class.getName().
replace(’.’, File.separatorChar) + ".class");
本謎題和前一個(gè)謎題的主要教訓(xùn)是:在使用不熟悉的類庫(kù)方法時(shí)一定要格外小心。當(dāng)你心存疑慮時(shí),就要求助于Javadoc。還有就是正則表達(dá)式是很棘手的:它所引發(fā)的問(wèn)題趨向于在運(yùn)行時(shí)刻而不是在編譯時(shí)刻暴露出來(lái)。
對(duì)API的設(shè)計(jì)者來(lái)說(shuō),使用方法具名的模式來(lái)以明顯的方式區(qū)分方法行為的差異是很重要的。Java的String類就沒(méi)有很好地遵從這一原則。對(duì)許多程序員來(lái)說(shuō),對(duì)于哪些字符串替代方法使用的是字面含義的字符串,以及哪些使用的是正則表達(dá)式或替代字符串,要記住這些都不是一件容易事。
謎題22:URL的愚弄
本謎題利用了Java編程語(yǔ)言中一個(gè)很少被人了解的特性。請(qǐng)考慮下面的程序?qū)?huì)做些什么?
public class BrowserTest {
public static void main(String[] args) {
System.out.print("iexplore:");
http://www.google.com;
System.out.println(":maximize");
}
}
這是一個(gè)有點(diǎn)詭異的問(wèn)題。該程序?qū)⒉粫?huì)做任何特殊的事情,而是直接打印iexplore::maximize。在程序中間出現(xiàn)的URL是一個(gè)語(yǔ)句標(biāo)號(hào)(statement label)[JLS 14.7]后面跟著一行行尾注釋(end-of-line comment)[JLS 3.7]。在Java中很少需要標(biāo)號(hào),這多虧了Java沒(méi)有g(shù)oto語(yǔ)句。在本謎題中所引用的“Java編程語(yǔ)言中很少被人了解的特性”實(shí)際上就是你可以在任何語(yǔ)句前面放置標(biāo)號(hào)。這個(gè)程序標(biāo)注了一個(gè)表達(dá)式語(yǔ)句,它是合法的,但是卻沒(méi)什么用處。
package com.javapuzzlers;
import java.io.File;
public class MeToo {
public static void main(String[] args){
System.out.println(MeToo.class.getName().
replaceAll("\\.", File.separator) + ".class");
}
}
這個(gè)程序根據(jù)底層平臺(tái)的不同會(huì)顯示兩種行為中的一種。如果文件分隔符是斜杠,就像在UNIX上一樣,那么該程序?qū)⒋蛴om/javapuzzlers/MeToo.class,這是正確的。但是,如果文件分隔符是反斜杠,就像在Windows上一樣,那么該程序?qū)⒋蛴∠裣旅孢@樣的內(nèi)容:
Exception in thread "main"
java.lang.StringIndexOutOfBoundsException: String index out of range: 1
at java.lang.String.charAt(String.java:558)
at java.util.regex.Matcher.appendReplacement(Mather.
java:696)
at java.util.regex.Matcher.replaceAll(Mather.java:806)
at java.lang.String.replaceAll(String.java:2000)
at com.javapuzzlers.MeToo.main(MeToo.java:6)
盡管這種行為是平臺(tái)相關(guān)的,但是它并非就是我們所期待的。在Windows上出了什么錯(cuò)呢?
事實(shí)證明,String.replaceAll的第二個(gè)參數(shù)不是一個(gè)普通的字符串,而是一個(gè)替代字符串(replacement string),就像在java.util.regex規(guī)范中所定義的那樣[Java-API]。在替代字符串中出現(xiàn)的反斜杠會(huì)把緊隨其后的字符進(jìn)行轉(zhuǎn)義,從而導(dǎo)致其被按字面含義而處理了。
當(dāng)你在Windows上運(yùn)行該程序時(shí),替代字符串是單獨(dú)的一個(gè)反斜杠,它是無(wú)效的。不可否認(rèn),拋出的異常應(yīng)該提供更多一些有用的信息。
那么你應(yīng)該怎樣解決此問(wèn)題呢?5.0版本提供了不是一個(gè)而是兩個(gè)新的方法來(lái)解決它。第一個(gè)方法是java.util.regex.Matcher.quoteReplacement,它將字符串轉(zhuǎn)換成相應(yīng)的替代字符串。下面展示了如何使用這個(gè)方法來(lái)訂正該程序:
System.out.println(MeToo.class.getName().replaceAll("\\.",
Matcher.quoteReplacement(File.separator)) + ".class");
引入到5.0版本中的第二個(gè)方法提供了一個(gè)更好的解決方案。該方法就是String.replace(CharSequence, CharSequence),它做的事情和String.replaceAll相同,但是它將模式和替代物都當(dāng)作字面含義的字符串處理。下面展示了如何使用這個(gè)方法來(lái)訂正該程序:
System.out.println(MeToo.class.getName().
replace(".", File.separator) + ".class");
但是如果你使用的是較早版本的Java該怎么辦?很遺憾,沒(méi)有任何捷徑能夠生成替代字符串。完全不使用正則表達(dá)式,而使用String.replace(char,char)也許要顯得更容易一些:
System.out.println(MeToo.class.getName().
replace(’.’, File.separatorChar) + ".class");
本謎題和前一個(gè)謎題的主要教訓(xùn)是:在使用不熟悉的類庫(kù)方法時(shí)一定要格外小心。當(dāng)你心存疑慮時(shí),就要求助于Javadoc。還有就是正則表達(dá)式是很棘手的:它所引發(fā)的問(wèn)題趨向于在運(yùn)行時(shí)刻而不是在編譯時(shí)刻暴露出來(lái)。
對(duì)API的設(shè)計(jì)者來(lái)說(shuō),使用方法具名的模式來(lái)以明顯的方式區(qū)分方法行為的差異是很重要的。Java的String類就沒(méi)有很好地遵從這一原則。對(duì)許多程序員來(lái)說(shuō),對(duì)于哪些字符串替代方法使用的是字面含義的字符串,以及哪些使用的是正則表達(dá)式或替代字符串,要記住這些都不是一件容易事。
謎題22:URL的愚弄
本謎題利用了Java編程語(yǔ)言中一個(gè)很少被人了解的特性。請(qǐng)考慮下面的程序?qū)?huì)做些什么?
public class BrowserTest {
public static void main(String[] args) {
System.out.print("iexplore:");
http://www.google.com;
System.out.println(":maximize");
}
}
這是一個(gè)有點(diǎn)詭異的問(wèn)題。該程序?qū)⒉粫?huì)做任何特殊的事情,而是直接打印iexplore::maximize。在程序中間出現(xiàn)的URL是一個(gè)語(yǔ)句標(biāo)號(hào)(statement label)[JLS 14.7]后面跟著一行行尾注釋(end-of-line comment)[JLS 3.7]。在Java中很少需要標(biāo)號(hào),這多虧了Java沒(méi)有g(shù)oto語(yǔ)句。在本謎題中所引用的“Java編程語(yǔ)言中很少被人了解的特性”實(shí)際上就是你可以在任何語(yǔ)句前面放置標(biāo)號(hào)。這個(gè)程序標(biāo)注了一個(gè)表達(dá)式語(yǔ)句,它是合法的,但是卻沒(méi)什么用處。