笔记

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 阅读(475) | 评论 (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="http://www.IBM.com/software">
使用的一些术语:
  • 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 阅读(293) | 评论 (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 阅读(1130) | 评论 (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 阅读(252) | 评论 (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 阅读(356) | 评论 (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 阅读(458) | 评论 (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 阅读(279) | 评论 (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 阅读(208) | 评论 (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 阅读(202) | 评论 (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 阅读(166) | 评论 (0)编辑 收藏
仅列出标题
共4页: 上一页 1 2 3 4 下一页 

导航

<2024年3月>
252627282912
3456789
10111213141516
17181920212223
24252627282930
31123456

统计

常用链接

留言簿

随笔分类

随笔档案

收藏夹

博客

搜索

最新评论

阅读排行榜

评论排行榜