筆記

          way

          Java Puzzlers(二-二 字符)

          19  單行注釋
              public static void main(String[] args) {
                  System.out.println(classify('n') + classify('+') + classify('2'));
              }
              static String classify(char ch) {
                  if ("0123456789".indexOf(ch) >= 0)
                      return "NUMERAL ";
                  if ("abcdefghijklmnopqrstuvwxyz".indexOf(ch) >= 0)
                      return "LETTER ";
                 /* (Operators not supported yet)
                  if ("+-*/&|!=".indexOf(ch) >= 0)
                      return "OPERATOR ";
                 */
                  return "UNKNOWN ";
              }
          編譯出錯,塊注釋不能嵌套,在注釋內的文本都不會被特殊對待。
          // Code commented out with an if statement - doesn't always work!
          if (false) {

              /* Add the numbers from 1 to n */
              int sum = 0;
              for (int i = 1; i <= n; i++)
                  sum += i;
          }
          這是語言規范推薦的一種條件編譯的技術,但不是非常適合注釋代碼。除非包含的語句都是有效的表達式,否則這種條件編譯不能用作注釋。最好的注釋代碼方法是用單行注釋。

          20   反斜杠
             Me.class.getName() 返回的是Me類的完整名,如"com.javapuzzlers.Me"。 
             System.out.println( Me.class.getName().replaceAll(".", "/") + ".class");
             應該得到com/javapuzzlers/Me.class?不對。問題出在String.replaceAll把正則表達式作為第一個參數,而不是字符。正則表達是“.”表示配對任何單獨的字符,所以類名的每一個字符都被斜線替代。為了只匹配句號,必須用反斜線(\)轉義。因為反斜線在字符串中有特殊意義——它是escape sequence的開始——反斜線自身也必須用一個反斜線轉義。
          正確:System.out.println( Me.class.getName().replaceAll("\\.", "/") + ".class");
          為了解決這類問題,java 5提供了一個新的靜態方法java.util.regex.Pattern.quote。用一個字符串作為參數,增加任何需要的轉義,返回一個和輸入字符串完全匹配的正則表達式字符串:
            System.out.println(Me.class.getName().replaceAll(Pattern.quote("."), "/") + ".class");
          這個程序的另外一問題就是依賴于平臺。不是所有的文件系統都是用斜線來組織文件。為了在你運行的平臺取得正確的文件名,你必須使用正確的平臺分隔符來替換斜線。

          21 
              System.out.println(MeToo.class.getName().
              replaceAll("\\.", File.separator) + ".class");
          java.io.File.separator 是一個公共的String 屬性,指定用來包含平臺依賴的文件名分隔符。在UNIX上運行打印com/javapuzzlers/MeToo.class。然而,在Windows上程序拋出異常:
              StringIndexOutOfBoundsException: String index out of range: 1
          結果是String.replaceAll 的第二個參數不是普通字符串而是一個在java.util.regex 規范中定義的 replacement string,反斜線轉義了后面的字符。當在Windows上運行的時候,替換字符是一個單獨的反斜線,無效。JAVA 5提供了兩個新方法來解決這個問題,一個是java.util.regex.Matcher.quoteReplacement,它替換字符串為相應的替換字符串:
           System.out.println(MeToo.class.getName().replaceAll(
             "\\.",  Matcher.quoteReplacement(File.separator))+".class");
          第二個方法提供了更好的解決方法。String.replace(CharSequence, CharSequence)和String.replaceAll做同樣的事情,但他把兩個參數都作為字符串處理:System.out.println(MeToo.class.getName().replace(".", File.separator) + ".class");   
          如果用的是java早期版本就沒有簡單的方法產生替換字符串。完全不用正則表達式,使用String.replace(char, char)跟容易一些:
          System.out.println(MeToo.class.getName().replace('.', File.separatorChar) + ".class");
          教訓:當用不熟悉的庫方法的時候,小心點。有懷疑的話,查看Javadoc。當然正則表達式也很棘手:他編譯時可能沒問題運行時卻更容易出錯。


          22  statement label
               認真寫注釋,及時更新。去掉無用代碼。如果有東西看起來奇怪不真實,很有可能是錯誤的。
          23
              private static Random rnd = new Random();
              public static void main(String[] args) {
                StringBuffer word = null;
                switch(rnd.nextInt(2)) {
                    case 1:  word = new StringBuffer('P');
                    case 2:  word = new StringBuffer('G');
                    default: word = new StringBuffer('M');
                }
                word.append('a');
                word.append('i');
                word.append('n');
                System.out.println(word);
             }
          在一次又一次的運行中,以相等的概率打印出Pain,Gain或 Main?答案它總是在打印ain。一共有三個bug導致這種情況。
          一是 Random.nextInt(int) ,看規范可知這里返回的是0到int值之間的前閉后開區間的隨機數。因此程序中永遠不會返回2。
          這是一個相當常見的問題源,被熟知為“柵欄柱錯誤(fencepost error)”。這個名字來源于對下面這個問題最常見的但卻是錯誤的答案,如果你要建造一個100英尺長的柵欄,其柵欄柱間隔為10英尺,那么你需要多少根柵欄柱呢?11根或9根都是正確答案,這取決于是否要在柵欄的兩端樹立柵欄柱,但是10根卻是錯誤的。要當心柵欄柱錯誤,每當你在處理長度、范圍或模數的時候,都要仔細確定其端點是否應該被包括在內,并且要確保你的代碼的行為要與其相對應。
          第二個bug是 case沒有配套的
          break。從5.0版本起,javac提供了-Xlint:fallthrough標志,當你忘記在一個case與下一個case之間添加break語句是,它可以生成警告信息。不要從一個非空的case向下進入了另一個case。這是一種拙劣的風格,因為它并不常用,因此會誤導讀者。十次中有九次它都會包含錯誤。如果Java不是模仿C建模的,那么它倒是有可能不需要break。對語言設計者的教訓是:應該考慮提供一個結構化的switch語句。
          最后一個,也是最微妙的一個bug是表達式new StringBuffer(‘M')可能沒有做哪些你希望它做的事情。StringBuffer(char)構造器根本不存在。StringBuffer有一個無參數的構造器,一個接受一個String作為字符串緩沖區初始內容的構造器,以及一個接受一個int作為緩沖區初始容量的構造器。在本例中,編譯器會選擇接受int的構造器,通過拓寬原始類型轉換把字符數值'M'轉換為一個int數值77[JLS 5.1.2]。換句話說,new StringBuffer(‘M')返回的是一個具有初始容量77的空的字符串緩沖區。該程序余下的部分將字符a、i和n添加到了這個空字符串緩沖區中,并打印出該字符串緩沖區那總是ain的內容。 為了避免這類問題,不管在什么時候,都要盡可能使用熟悉的慣用法和API。如果你必須使用不熟悉的API,那么請仔細閱讀其文檔。在本例中,程序應該使用常用的接受一個String的StringBuffer構造器。

          posted on 2010-10-30 13:46 yuxh 閱讀(479) 評論(0)  編輯  收藏 所屬分類: jdk

          導航

          <2010年10月>
          262728293012
          3456789
          10111213141516
          17181920212223
          24252627282930
          31123456

          統計

          常用鏈接

          留言簿

          隨筆分類

          隨筆檔案

          收藏夾

          博客

          搜索

          最新評論

          閱讀排行榜

          評論排行榜

          主站蜘蛛池模板: 乌兰察布市| 石城县| 和平区| 中江县| 正阳县| 积石山| 信宜市| 长白| 青岛市| 汤阴县| 姜堰市| 淄博市| 莲花县| 上蔡县| 祁门县| 芜湖市| 江华| 兴海县| 天柱县| 泸溪县| 东阳市| 双江| 尼勒克县| 武定县| 景德镇市| 柳林县| 措美县| 沁阳市| 紫云| 米脂县| 永德县| 和顺县| 辰溪县| 阜康市| 怀化市| 冕宁县| 中卫市| 乌兰浩特市| 东海县| 天等县| 麻城市|