筆記

          way

          來自

          http://stackoverflow.com/questions/1099300/whats-the-difference-between-getpath-getabsolutepath-and-getcanonicalpath

          C:\temp\file.txt" - this is a path, an absolute path, a canonical path

          .\file.txt This is a path, It's not an absolute path nor canonical path.

          C:\temp\myapp\bin\..\\..\file.txt This is a path, and an absolute path, it's not a canonical path

          Canonical path is always an absolute path.

          Converting from a path to a canonical path makes it absolute (通常會處理改變當前目錄,所以像. ./file.txt 變為c:/temp/file.txt). The canonical path of a file just "purifies" the path, 去除和解析類似“ ..\” and resolving symlinks(on unixes)

          In short:

          • getPath() gets the path string that the File object was constructed with, and it may be relative current directory.
          • getAbsolutePath() gets the path string after resolving it against the current directory if it's relative, resulting in a fully qualified path.
          • getCanonicalPath() gets the path string after resolving any relative path against current directory, and removes any relative pathing (. and ..), and any file system links to return a path which the file system considers the canonical means to reference the file system object to which it points.

          Also, each of this has a File equivalent which returns the corresponding File object.

          The best way I have found to get a feel for things like this is to try them out:

          import java.io.File;
          public class PathTesting {
                  public static void main(String [] args) {
                          File f = new File("test/.././file.txt");
                          System.out.println(f.getPath());
                          System.out.println(f.getAbsolutePath());
                          try {
                                  System.out.println(f.getCanonicalPath());
                          }
                          catch(Exception e) {}
                  }
          }

          Your output will be something like:

          test\..\.\file.txt
          C:\projects\sandbox\trunk\test\..\.\file.txt
          C:\projects\sandbox\trunk\file.txt

          So, getPath() gives you the path based on the File object, which may or may not be relative; getAbsolutePath() gives you an absolute path to the file; and getCanonicalPath() gives you the unique absolute path to the file. Notice that there are a huge number of absolute paths that point to the same file, but only one canonical path.

          When to use each? Depends on what you're trying to accomplish, but if you were trying to see if two Files are pointing at the same file on disk, you could compare their canonical paths.
          posted @ 2011-06-24 13:42 yuxh 閱讀(508) | 評論 (0)編輯 收藏

          DTDs

          Introduced as part of the XML 1.0 specification, DTDs are the oldest constraint model around in the XML world. They're simply to use, but this simplicity comes at a price: DTDs are inflexible, and offer you little for data type validation as well.

          XML Schema (XSD)

          XML Schema is the W3C's anointed successor to DTDs. XML Schemas are literally orders of magnitude more flexible than DTDs, and offer an almost dizzying array of support for various data types. However, just as DTDs were simple and limited, XML Schemas are flexible, complex, and (some would argue) bloated. It takes a lot of work to write a good schema, even for 50- or 100-line XML documents. For this reason, there's been a lot of dissatisfaction with XML Schema, even though they are widely being used.

          [prefix]:[element name]
          元素:
          root元素必須包含所有文檔中的元素,只能有一個root元素。元素名只能以下劃線或字母開頭,不能有空格,區分大小寫。開元素必須有對應閉元素(也有類似html的簡寫,如<img src="/images/xml.gif" />)。文檔由DTD或schema來限制它是否合格。
          屬性:
          什么時候用屬性?基本原則:多個值的數據用元素,單值的數據用元素。如果數據有很多值或者比較長,數據最可能屬于元素。他主要被當作文本,容<rss:author>Doug Hally</rss:author> <journal:author>Neil Gaiman</journal:author>易搜索,好用。比如一本書的章節描述。然而如果數據主要作為單值處理的話,最好作為屬性。如果搞不清楚,可以安全的使用元素。
          命名空間Namespaces:
          xml的命名空間是一種用一個特定的URI來關聯XML文檔里的一個或多個元素的方法。意味著元素是由名字和命名空間一起來識別的。許多復雜的XML文件里,同一個名字會有多個用途。比如,一個RSS feed有一個作者,這個作者同時是每個日記的。雖然這些數據都用author元素來表示,但他們不應該被當作同一個類型的數據。命名空間很好的解決了這個問題,命名空間說明書要求一個前綴和唯一的URI聯合起來區分不同命名空間里的元素。如http://www.neilgaiman.com/entries作為URI,聯合前綴journal用來表示日志相關的元素。
          <rdf:RDF xmlns:rss="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:journal="http://www.neilgaiman.com/entries">,然后就可使用了:
          <rss:author>Doug Hally</rss:author> <journal:author>Neil Gaiman</journal:author>實際上在使用命名空間前綴的時候再定義也可以的:
          <rss:author xmlns:rss="http://www.w3.org/1999/02/22-rdf-syntax-ns#">Doug Hally</rss:author>
          如果名字沒有命名空間,不代表在默認命名空間中,而是xml處理器以在任何命名空間以外方式解釋他。要聲明默認命名空間的話就不用后面冒號部分,如<Insured xmlns="使用的一些術語:
          • The name of a namespace (such as http://www.ibm.com/software) is the namespace URI.

          • The element or attribute name can include a prefix and a colon (as in prod:Quantity). A name in that form is called a qualified name, or QName, and the identifier that follows the colon is called a local name. If a prefix is not in use, neither is the colon, and the QName and local name are identical.

          • An XML identifier (such as a local name) that has no colon is sometimes called an NCName. (The NC comes from the phrase no colon.)

          Entity references:
          用來處理轉義字符,語法是& [entity name] ;XML解析器碰到這種entity reference,就會用對應的值替換掉他。如&lt;(<),&gt;(>),&amp;(&),&quot;("),&apos;(')。注意entity reference是用戶可定義的。比如多處用到版權提示,自己定義后以后更改就方便了:<ora:copyright>&OReillyCopyright;</ora:copyright>。除了用來表示數據中的復雜或特殊字符外,entity reference還會有更多用途。

          不解析的數據
          當傳輸大量數據給應用且不用xml解析的時候,CDATA就有用了。當大量的字符需要用entity reference轉義的時候,或空格必須保留的時候,使用CDATA。<![CDATA[….]]>

          posted @ 2011-03-24 09:05 yuxh 閱讀(318) | 評論 (0)編輯 收藏
          轉自http://www.bennadel.com/blog/1504-Ask-Ben-Parsing-CSV-Strings-With-Javascript-Exec-Regular-Expression-Command.htm,把csv文件按分隔符切割后放在數組中。
          // This will parse a delimited string into an array of arrays. 
             // The default delimiter is the comma, but this

          // can be overriden in the second argument.
              CSVToArray:function(strData, strDelimiter){
           
                  
          // Check to see if the delimiter is defined. If not,
                  // then default to comma.
                  strDelimiter = (strDelimiter || ",");
                  
          // Create a regular expression to parse the CSV values.
                  var objPattern = new RegExp(
                      (
                          
          // Delimiters.
                          "(\\" + strDelimiter + "|\\r?\\n|\\r|^)" +
           
                          
          // Quoted fields.
                          "(?:\"([^\"]*(?:\"\"[^\"]*)*)\"|" +
           
                          
          // Standard fields.
                          "([^\"\\" + strDelimiter + "\\r\\n]*))"
                      ),
                      
          "gi"
                      );
           
           
                  // Create an array to hold our data. Give the array
                  // a default empty first row.
                  var arrData = [[]];
           
                  // Create an array to hold our individual pattern
                  // matching groups.
                  var arrMatches = null;
           
           
                  // Keep looping over the regular expression matches
                  // until we can no longer find a match.
                  while (arrMatches = objPattern.exec( strData )){
           
                      // Get the delimiter that was found.
                      var strMatchedDelimiter = arrMatches[ 1 ];
           
                      // Check to see if the given delimiter has a length
                      // (is not the start of string) and if it matches
                      // field delimiter. If id does not, then we know
                      // that this delimiter is a row delimiter.
                      if (
                          strMatchedDelimiter.length &&
                          (strMatchedDelimiter != strDelimiter)
                          ){
           
                          // Since we have reached a new row of data,
                          // add an empty row to our data array.
                          arrData.push( [] );
                      }
           
           
                      // Now that we have our delimiter out of the way,
                      // let's check to see which kind of value we
                      // captured (quoted or unquoted).
                      if (arrMatches[ 2 ]){
           
                          // We found a quoted value. When we capture
                          // this value, unescape any double quotes.
                          var strMatchedValue = arrMatches[ 2 ].replace(
                              new RegExp( 
          "\"\"""g" ),
                              
          "\""
                              );
           
                      }
           else {
           
                          
          // We found a non-quoted value.
                          var strMatchedValue = arrMatches[ 3 ];
                      }

           
           
                      
          // Now that we have our value string, let's add
                      // it to the data array.
                      arrData[ arrData.length - 1 ].push( strMatchedValue );
                  }
           
                  
          // Return the parsed data.
                  return( arrData );
              }
          posted @ 2010-12-03 09:06 yuxh 閱讀(1157) | 評論 (0)編輯 收藏

          31
          while (i != 0)
              i >>>= 1; //無符號右移,不管正負左邊都是補0
          為了表達式合法,這里的i必須是整型(byte, char, short, int, or long)。謎題的關鍵在于>>>= 是一個復合賦值操作符,不幸的是復合賦值操作符會默默的做narrowing primitive conversions,即從一個數據類型轉換為一個更小的數據類型。Narrowing primitive conversions can lose information about the magnitude or precision of numeric values。為了使問題更具體,假設這樣定義:
          short i = -1; 因為初始值i ((short)0xffff) 非零,循環執行。第一步位移會把i提升為int。short, byte, or char類型的操作數都會做這樣的操作。這是widening primitive conversion,沒有信息丟失。這種提升有符號擴展,因此結果是int值0xffffffff。無符號右移一位產生int值0x7fffffff。為了把int值存回short變量,Java執行了可怕的narrowing primitive conversion,即簡單去掉高十六位。這樣又變回了(short)0xffff。如果定義類似short or byte型的負數,都會得到類似結果。你如果定義的是char話,則不會無限循環,因為char值非負,位移之前的寬擴展不會做符號擴展。

          總結:不要在short, byte, or char變量上使用復合賦值操作符。這種表達式進行混合類型計算,非常容易混淆。更糟糕的是隱含的窄映射會丟掉信息。

          32
          while (i <= j && j <= i && i != j) {}
          i <= j and j <= i, surely i must equal j?對于實數來說是這樣的。他非常重要,有個名稱:The ≤ relation on the real numbers is said to be antisymmetric。Java's <= operator used to be antisymmetric before release 5.0, but no longer.Java 5之前數據比較符號(<, <=, >, and >=) 需要兩邊的操作數必須為基礎類型(byte, char, short, int, long, float, or double).在Java 5變為兩邊操作數為凡是可轉變為基礎類型的類型。java 5引入autoboxing and auto-unboxing 。The boxed numeric types are Byte, Character, Short, Integer, Long, Float, and Double。具體點,讓上面進入無限循環:
          Integer i = new Integer(0);
          Integer j = new Integer(0);
          (i <= j and j <= i) perform unboxing conversions on i and j and compare the resulting int values numerically。i和j表示0,所以表達式為true。i != j比較的是對象引用,也為true。很奇怪規范沒有把等號改為比較值。原因很簡單:兼容性。當一種語言廣泛應用的時候,不能破壞已存在的規范來改變程序的行為。System.out.println(new Integer(0) == new Integer(0));總是輸出false,所以必須保留。當一個是boxed numeric 類型,另一個是基本類型的時候可以值比較。因為java 5之前這是非法的,具體點:
          System.out.println(new Integer(0) == 0); //之前的版本非法,Java 5輸出True
          總結:當兩邊的操作數是boxed numeric類型的時候,數字比較符和等于符號是根本不同的:數字比較符是值比較,等號比較的是對象引用

          33
          while (i != 0 && i == -i) {}
          有負號表示i一定是數字,NaN不行,因為他不等于任何數。事實上,沒有實數可以出現這種情況。但Java的數字類型并沒有完美表達實數。浮點數由一個符號位,一個有效數字(尾數),一個指數構成。浮點數只有0才會和自己的負數相等,所以i肯定是整數。有符號整數用的是二進制補碼計算:取反加一。補碼的一大優勢是用一個唯一的數來表示0。然而有一個對應的缺點:本來可表達偶數個值,現在用一個表達了0,剩下奇數個來表示正負數,意味著正數和負數的數量不一樣。比如int值,他的Integer.MIN_VALUE-231)。十六進制表達0x8000000。通過補碼計算可知他的負數仍然不變。對他取負數是溢出了的,不過Java在整數計算中忽略了溢出。
          總結:Java使用二進制補碼,是不對稱的。有符號整數(int, long, byte, and short) 負數值比整數值多一個。

          34
                  final int START = 2000000000;
                  int count = 0;
                  for (float f = START; f < START + 50; f++)
                      count++;
                  System.out.println(count);
          注意循環變量是float。回憶謎題28 ,明顯f++沒有任何作用。f的初始化值接近Integer.MAX_VALUE,因此需要31位來準確表達,但是float類型只提供了24位精度。增加這樣大的一個float值不會改變值。看起來會死循環?運行程序會發現,輸出0。循環中f和(float)(START + 50)做比較。但int和float比較的時候,自動把int先提示為float。不幸的是三個會引起精度丟失的寬基本類型轉換的其中之一(另外兩個是long到float,long到double)。f的初始值巨大,加50再轉換為float和直接把f轉換為float是一樣的效果,即(float)2000000000 == 2000000050,所以f < START + 50 失敗。只要把float改為int即可修正。
               沒有計算器,你怎么知道 2,000,000,050 和float表示2,000,000,000一樣?……
          The moral of this puzzle is simple: Do not use floating-point loop indices, because it can lead to unpredictable behavior. If you need a floating-point value in the body of a loop, take the int or long loop index and convert it to a float or double. You may lose precision when converting an int or long to a float or a long to a double, but at least it will not affect the loop itself. When you use floating-point, use double rather than float unless you are certain that float provides enough precision and you have a compelling performance need to use float. The times when it's appropriate to use float rather than double are few and far between。

          35
          下面程序模擬一個簡單的時鐘
          int minutes = 0;
          for (int ms = 0; ms < 60*60*1000; ms++)
            if (ms %  60*1000 == 0)
               minutes++;
           
          System.out.println(minutes);
          結果是60000,問題在于布爾表達式ms % 60*1000 == 0,最簡單的修改方法是:if (ms % (60 * 1000) == 0)
          更好的方法是用合適命名的常量代替魔力數字

          private static final int MS_PER_HOUR = 60 * 60 * 1000;
          private static final int MS_PER_MINUTE = 60 * 1000;
          public static void main(String[] args) {
             int minutes = 0;
             for (int ms = 0; ms < MS_PER_HOUR; ms++)
               if (ms % MS_PER_MINUTE == 0)
                  minutes++;
              System.out.println(minutes);
          }
          絕不要用空格來分組;使用括號來決定優先級

          posted @ 2010-11-15 19:59 yuxh 閱讀(279) | 評論 (0)編輯 收藏
          24     
            for (byte b = Byte.MIN_VALUE; b < Byte.MAX_VALUE; b++) {
                  if (b == 0x90)
                      System.out.print("Joy!");
              }
          Ox90 超過了byte的取值范圍-128到127。byte和int比較是一種混合類型比較。考慮個表達式((byte)0x90 == 0x90)得到的是false。byte和int做比較的時候,Java先對byte進行了widening primitive conversion 再比較兩個int值。因為byte是有符號類型,轉變做了符號擴展,把負的byte值轉換為相應的int值。這個例子中(byte)0x90被轉變為-112,當然不等于int值 0x90或者說+144。混合比較總讓人迷惑,因為總是強迫系統去提升一個操作數來和另一種類型匹配。有幾種方式可避免混合比較。可以把int映射為byte,之后比較兩個byte值:
          if (b == (byte)0x90)
              System.out.println("Joy!");
          另外,可用mask抑制符號擴展,把byte轉換為int,之后比較兩個int值:
          if ((b & 0xff) == 0x90)
              System.out.println("Joy!");
          但最好的方法是把常量值移出循環放到常量聲明中。
              private static final byte TARGET = 0x90; // Broken!
              public static void main(String[] args) {
                  for (byte b = Byte.MIN_VALUE; b < Byte.MAX_VALUE; b++)
                      if (b == TARGET)
                          System.out.print("Joy!");
              }
          不幸的是,上面編譯通不過:0x90對于byte類型來說不是一個有效值。這樣修改即可:
          private static final byte TARGET = (byte)0x90;

          To summarize: Avoid mixed-type comparisons, because they are inherently confusing (Puzzle 5). To help achieve this goal, use declared constants in place of "magic numbers." You already knew that this was a good idea; it documents the meanings of constants, centralizes their definitions, and eliminates duplicate definitions.現在你知道他還可以強制你為每一個常量定義適用的類型,避免一種混合類型比較的來源。

          25
          int j = 0;
          for (int i = 0; i < 100; i++)
            j = j++;
          System.out.println(j); //
          打印出的是0

          問題出在 j = j++; 等同于下列操作:

          int tmp = j; j = j + 1; j = tmp;
          這次的教訓和難題7一樣:在一個表達式中不要給同一個變量賦值超過一次。

          26

          public static final int END = Integer.MAX_VALUE;
          public static final int START = END - 100;
          public static void main(String[] args) {
              int count = 0;
              for (int i = START; i <= END; i++)
                 count++;
               System.out.println(count);
          }
          看起來像100,再仔細看循環是小于等于,應該是101?結果是程序沒有輸出任何值,陷入一個死循環。問題出在Integer.MAX_VALUE,當繼續增加的時候,他悄悄變為Integer.MIN_VALUE。如果你需要循環int值邊界,最好用long變量做索引:
          for (long i = START; i <= END; i++)  //輸出101
          教訓是:ints are not integers。無論何時用基本類型,注意邊界值。上溢或下溢會出現什么情況?一般來說最好用大一點的類型(基本類型是byte, char, short, int, and long)。也可以不用long:

          int i = START;
          do {
            count++;
          } while (i++ != END);
          考慮到清晰和簡單,總是用long索引,除了一種特殊情況:如果要遍歷所有int值,這樣用int索引的話會快兩倍。
          一個循環四十億int值,調用方法的常規用法:

          // Apply the function f to all four billion int values
          int i = Integer.MIN_VALUE;
          do {
          f(i);
          } while (i++ != Integer.MAX_VALUE);
          

          27  位移
          記住java是使用二進制補碼計算,在任何有符號基本類型中(byte, short, int, or long)都是用所有位置1來表示-1。
                  int i = 0;
                  while (-1 << i != 0)       //左位移
                      i++;
                  System.out.println(i);
          int型的-1用0xffffffff 表示。不斷左移,右邊由0補位。移位32次,變為全0,跳出循環打印32?實際上程序會死循環。問題出在-1<<32不等于0而是等于-1,因為位移符號只用右邊操作數的低五位作為移動距離,如果左操作數是long的話用六位。三個位移操作符:<<,>>,>>>都是這樣。移動距離總是0到31,左邊操作數是long的話0到63。位移距離用32取模,左邊是long則用64取模。給int值位移32位或給long值位移64位只會返回本身。所以不可能用位移完全移除一個數據。幸運的是,有一個簡單的辦法解決這個問題。保存上一次的位移結果,每一次迭代多移動一位。
                  int distance = 0;
                  for (int val = -1; val != 0; val <<= 1)
                      distance++;
                  System.out.println(distance);
          修改后的程序說明了一個原則:位移距離如果可能的話,用常量
          另外一個問題,許多程序員認為右移一個負的移動距離,就和左移一樣,反之亦然。事實上不是這樣,左移是左移,右移就是右移。負數距離只留下低五位(long留六位),其余的置0就變為了正數距離。比如,左移一個int值-1的距離,實際上是左移31位。

          28 無窮的表示
          for (int i = start; i <= start + 1; i++) {
          }
          看起來循環兩次就會結束,如果這樣定義呢:
          int start = Integer.MAX_VALUE - 1;//死循環
          while (i == i + 1) {
          }
          這個不可能死循環?如果i是無窮呢?Java采用IEEE 754浮點數算術,用double或float來表示無窮。所以可以用任何浮點數計算表達式得出無窮來初始化i。比如:double i = 1.0 / 0.0;
          更好的是可以利用標準庫提供的常量:double i = Double.POSITIVE_INFINITY;
          事實上,根本用不著用無窮初始化i來引起死循環。只要足夠大的浮點數就夠了:double i = 1.0e40;
          這是因為浮點數越大,他的值和下一個數的值距離也就越大。distribution of floating-point values is a consequence of their representation with a fixed number of significant bits. 給足夠大的浮點數加一不會改變值,因為他不能填充這個數和下一個數之間的距離。浮點數操作返回最接近準確數學結果的浮點值。一旦兩個相鄰浮點值之間的距離大于2,加1就不會有效果。float類型來說,超過225(或33,554,432)再加1就無效;對double來說超過254(接近1.8 x 1016)再加1就無效。
               相鄰浮點數之間的距離稱為ulp(unit in the last place的縮寫)。在Java 5里 Math.ulp方法被引入來計算float或double值的ulp。

          總結:不可能用float或double來表示無窮。另外,在一個大的浮點數上加一個小的浮點數,值不會改變。有點不合常理,實數并不是這樣。記住二進制浮點數計算只是近似于實數計算。

          29 NaN

          while (i != i) { }
          IEEE 754 浮點數計算保留了一個特殊值來來表示不是數字的數量。NaN是浮點計算不能很好定義的數,比如0.0 / 0.0。規范定義NaN不等于任何數包括自己。因此double i = 0.0 / 0.0; 可讓開始的等式不成立。也有標準庫定義的常量:double i = Double.NaN; 如果一個或多個操作數為NaN,那么浮點數計算就會等于NaN。

          總結:float和doule存在特殊的值NaN,小心處理。

          30
          while (i != i + 0) { } 這一次不能使用float或者double。
          +操作符除了數字,就只能處理String。+操作符會被重載:對于String類型,他做的是連接操作。如果操作數有非String類型,會先做轉換變為String之后再做連接。i一般用作數字,要是對String型變量這么命名容易引起誤解。

          總結:操作符重載非常容易誤導人。好的變量名,方法名,類名和好的注釋對于程序的可讀性一樣重要。

          posted @ 2010-11-15 16:39 yuxh 閱讀(381) | 評論 (0)編輯 收藏
          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 @ 2010-10-30 13:46 yuxh 閱讀(480) | 評論 (0)編輯 收藏

          What Is JDBC ?

            JDBC 是java編程中一系列允許簡單連接到很多數據庫(特別是關系型數據庫)編程APIs . In Java 2 Platform, Standard Edition (J2SE) 5.0,
          JDBC API 由兩個包定義:

          java.sql :提供java訪問處理貯存在數據源(特別是關系型數據庫)中的數據,有最基礎常用的對象如Connection, ResultSet, Statement, and PreparedStatement。這個包j2se 和j2ee平臺都可使用。

          javax.sql:提供java訪問處理服務器端數據源。這個包給j2ee提供服務,如DataSource 和RowSet。

          ODBC bridge是以ODBC標準 C API 方式實現JDBC 的庫。

          簡而言之,JDBC是一個和database-independent 的訪問數據庫的API。

          DriverManager是唯一可以創建數據庫連接的類。DriverManager根據各個廠商(如Oracle, MySQL, and Sybase)提供的驅動創建數據庫。

          What Is ODBC ?

            Open Database Connectivity (ODBC) 是一種來自微軟的編程接口,他為Windows應用程序訪問網絡上數據庫提供了通用語言。 ODBC is a C-based interface
          to SQL-based database systems. It provides a consistent interface for communicating with a database and for accessing database metadata (information about the database system vendor and how the tables, views, and data are stored).ODBC作為標準出現。廠商為各自的DBMS提供了各種的驅動或bridges。從java客戶端訪問ODBC-based數據庫,可以使用JDBC-ODBC bridge,因此可以使用JDBC-ODBC bridge訪問支持ODBC的數據庫,比如Microsoft Access。微軟為他的操作系統提供ODBC driver manager。ODBC driver manager協調訪問ODBC驅動和對應的數據源。

          問題:如果用c++寫數據庫客戶端,你不得不在另一平臺重新寫客戶端;PC版的不能在Macintosh上運行。兩個原因:1.c++不是跨平臺的,很多東西沒有特別指定(如int型用多少位表示)2 更重要的是,想網絡訪問,GUI框架庫等在各個平臺不同。ODBC的另一個問題是,接口復雜學習時間長。JDBC去除了這些問題,為訪問關系數據庫引入平臺無關的解決方案。因為性能問題和缺少事務支持, JDBC-ODBC bridge 驅動只適合實驗用或沒有其他可選方法。

          What Is a JDBC-ODBC Bridge?

             簡而言之,JDBC-ODBC bridge通過大多數ODBC驅動來提供JDBC訪問。它是一個把JDBC操作轉換為ODBC操作的JDBC驅動。(ODBC操作是由 C-based libraries實現的——ODBC功能仍然在二進制代碼庫中;如果數據庫或硬件平臺更換,需要替換ODBC庫)。brige作為sun.jdbc.odbc包實現,包含一個native library用來訪問ODBC。sun.jdbc.odbc包在/jre/lib/rt.jar中,包含一個sun.jdbc.odbc.JdbcOdbcDriver類,用來JDBC驅動。注意,JDBC-ODBC bridge是一種“萬能”的方式,因此可能比一些特別設計的JDBC驅動慢。

          SQL is a Data Manipulation Language (DML—影響數據庫對象內容的命令集) and a Data Definition Language (DDL—影響數據庫對象結構的命令集).SQL also 提供控制事務命令 (such as commit and rollback)

          連接jdbc的過程參考JDBC加載分析 。總之JDBC驅動的作用是提供各種數據庫的具體實現(實現了java.sql.Driver接口),隱藏具體數據庫的細節(每個數據庫廠商可能會為同一個數據庫提供不止一個驅動,這些效率,價格/性能會有不同)。

          在fianlly中立刻關閉/釋放 JDBC資源(such as the ResultSet, Statement, PreparedStatement, and Connection objects),而不是等他們自己關閉,會改進應用程序的性能。寫一個工具類釋放這些資源是一個好辦法。

          JDBC API主要用來傳SQL statement給數據庫,但也能讀寫表格式數據源的數據,這種來自javax.sql.RowSet組接口的讀寫能力可以被定制去使用更新spreadsheet,flat file 類似表格式數據源的數據。

          JDBC有四種類型的驅動連接數據庫。

          異常:SQLException:有getNextException()可以鏈接一系列異常,還有很多方法可以展示額外的錯誤/異常信息。SQLWarning:SQLException的子類,表示非致命可忽略BatchUpdateException:批量更新時出現的錯誤,除了SQLException提供的信息,還有錯誤發生前已成功執行多少條數據DataTruncation:意外truancate 數據拋出。

          posted @ 2010-04-30 13:54 yuxh 閱讀(321) | 評論 (0)編輯 收藏

          Java Naming and Directory Interface (JNDI) is an API that supports accessing naming and directory services in Java programs.

          命名服務目的:把命名和對象聯系起來,提供用命名訪問對象的方法。

          目錄服務:允許屬性和對象聯系,比如用戶對象的email地址屬性,(命名服務不提供),因此能利用目錄服務訪問對象屬性或以屬性為基礎查找對象。

          posted @ 2010-04-29 14:59 yuxh 閱讀(231) | 評論 (0)編輯 收藏

          HTTP在TCP/IP的頂層,他是一種有web特性的網絡協議。HTTP會話結構是一種簡單的請求/響應序列;瀏覽器請求,服務器響應 。HTTP 響應可以 包含HTML,HTTP在響應內容(服務器返回的任何東西)之上添加頭信息。瀏覽器利用頭信息來幫助處理html頁面。把hml內容看作粘貼在HTTP響應中的數據。HTTP請求中有幾個方法,最常用的是POST和GET(區別)。HTTP響應中包含狀態代碼(如404),內容類型(也稱為MIME類型,他告訴瀏覽器將會收到什么類型的數據以便處理,比如展示圖片,提供html),響應的真實內容(html,圖片等)。

          posted @ 2010-04-02 16:16 yuxh 閱讀(224) | 評論 (0)編輯 收藏
          接口的方法默認為public,不能定義為private。
          If a class implements two or more interfaces that call for methods with identical signatures,we need only implement one such method in the implementing class—that method will do “double duty” in satisfying both interfaces’ implementation requirements as far as the compiler is concerned.
          定義 屬性,方法參數,返回類型的時候盡可能使用接口,客戶端代碼調用這樣的類會更加靈活。
          List和Set都是Collection接口的子類,使用Collection可以更通用。
              如果一定要自己創建集合類而且不通過擴展存在的ArrayList等的話,至少實現Collection接口,這樣才能在使用Collection的環境使用。
              靜態方法不能為abstract,不能調用非靜態的屬性或方法。我們經常利用靜態方法,屬性實現一些“工具類”,比如java.lang中的Math.
              接口不允許定義變量,除了定義public static final 變量來作為全局常量。但是final類型的變量必須顯示初始化,且初始化的方法必須是在申明時或者在構造方法中直接賦值,而不能通過調用函數賦值。
              j2se 5引入 :import static Administrator.*; 這樣在代碼中可以直接使用Administrator類的靜態變量。
          查詢了下關于是否用final限定方法參數以及局部變量的問題,有爭議(http://stackoverflow.com/questions/316352?sort=votes#sort-top),類似習慣問題,不過對傳入的參數重新賦值不是好習慣!否則在方法中使用該參數的時候你會考慮前面的代碼是否對參數處理過,還有可能失誤的進行了賦值。傾向于方法參數使用final,局部變量不使用。折中的辦法是設置eclipse的重賦值警告。
          posted @ 2009-12-16 11:16 yuxh 閱讀(186) | 評論 (0)編輯 收藏
          僅列出標題
          共4頁: 上一頁 1 2 3 4 下一頁 

          導航

          <2025年6月>
          25262728293031
          1234567
          891011121314
          15161718192021
          22232425262728
          293012345

          統計

          常用鏈接

          留言簿

          隨筆分類

          隨筆檔案

          收藏夾

          博客

          搜索

          最新評論

          閱讀排行榜

          評論排行榜

          主站蜘蛛池模板: 得荣县| 兰坪| 云浮市| 巴南区| 西安市| 来宾市| 芜湖县| 拉萨市| 滨海县| 广元市| 临江市| 柘荣县| 横峰县| 连平县| 松潘县| 清河县| 柳林县| 博客| 达日县| 马关县| 柳州市| 广丰县| 浦江县| 娱乐| 佛山市| 汤阴县| 呼伦贝尔市| 亚东县| 浦江县| 隆德县| 肥城市| 察雅县| 昌乐县| 白河县| 磐安县| 漯河市| 南宁市| 长顺县| 师宗县| 囊谦县| 亚东县|