Java 语法总结 -  基本数据类型 
 
Java 不是纯的面向对象的语言,不纯的地方就是这些基本数据类型不是对象。当然初
期 Java 的运行速度很慢,基本数据类型能在一定程度上改善性能。如果你想编写纯的
面向对象的程序,用包装器类是取代基本数据类型就可以了。 
 
1、基本类型的存储空间。byte--8 位,short--16 位,int--32 位,long--64 位,
float--32 位,double--64 位。这六种数字类型都是有符号的。固定的存储空间正是
Java 可移植性、跨平台的原因之一! 
 
2、基本类型的存在导致了 Java OOP 的不纯粹性。因为基本类型不是对象,一切皆对
象是个小小的谎言。这是出于执行效率的权衡。 
 
3、使用公式-2 的(位数-1)次幂到2 的(位数-1)次幂-1 确定整数类型的范围(byte、
short、int、long)。 
 
4、char 是 16 位 Unicode 字符或者说是 16 位无符号整数,范围从 0 到 65535。即
便如此,可以强制转换非法的数据,如:char c1 = (char) 10000;  char c2 = (char) 
-200;。可以从二进制存储的角度理解这点。 
 
5、整数有八进制(以 0 开头的整数)、十进制、十六进制(以 0x 或 0X 开头的整数)
表示。 
 
6、char 可以用单引号表示单个字符,如:'良'。也可以用 unicode 值'"ucafe'(四位
十六进制数)。 
 
7、布尔型 boolean。布尔型只能是 true 或者 false,并且测试它为真还是假。它不能
进行任何其他的运算,或者转化为其他类型。 
正例:boolean b1 = 1 > 2;     反例:int seen = button.isVisible(); 
实践:简洁是美德,请不要这样写:if ( is == true && done == false )  ,只有新
手才那么写。 
对于任何程序员 if ( whether && !done )  都不难理解吧。所以去掉所有的==fasle 
和 ==true 。 
 
8、默认的浮点类型是双精度(double),要想要一个 float 必须在浮点数后面加 F 或
者 f。如:float pi = 3.14;是错误的。 
 
9、默认的整数类型是 int 型,要想使用长整型可在后面加“l”或“L”,如:1000L。(小
写 l 容易被误认为 1,不推荐用) 
 
10、float 可以精确到 7 位有效数字,第 8 位的数字是第 9 位数字四舍五入上取得的;
double 可以精确到 16 位有效数字,第 17 位的数字是第 18 位数字四舍五入上取得的。
盖茨到底有多少钱?要用 double 表示,用 float 是装不下的…… 
 
PDF 文件使用 "pdfFactory" 试用版本创建 www.fineprint.cn
、
 int
、
long--0  float--0.0f 
11、如果要求精确的答案,请不要使用 float 和 double,因为它们是为了在广域数值
范围上提供较为精确的快速近似运算而精心设计的。然而,它们没有提供完全精确的结
果。尤其是对货币计算尤为不适合,因为要让一个 float 或 double 精确地表达 0.1(或
者 10 的任何) 
 
12、BigInteger 支持任意精度的整数。BigDecimal 支持任意精度的定点数。 
 
13、初始化无论怎么强调都不过分!Java 为所有的成员变量提供了默认初始化:byte、
short
double--0.0  boolean--false  char--'"u0000' ,特别地对象类型的引用全被初始化
为 null。(注意!除了数组之外的局部变量是得不到这种优待的,需要你自己初始化。
另外,默认初始化的值是你想要的吗?所以最好明确地对变量进行初始化,一般是在构
造函数中。) 
 
14、基本类型之间的转化。Java 的类型检查很严格,从低精度转换到高精度是无须显
式转换的,double d = 123;。但是反过来,进行窄化转换,由高精度向低精度,或者
一种类型到另一种类型,则必须使用强制类型转化。Java 提供了安全转化机制,但是
结果是否是期望的,你自己保证吧。 
double d = 12.5; 
float f = (int) d; //结果不是 13,而是 12! 
浮点型转化为整型时,不进行四舍五入,直接截断小数点后面的数。 
 
15、提升。各种基本数据类型进行混合运算,结果会是表达能力最强的那种。如:int
和 long 运算,结果是 long,整型和浮点型运算结果是浮点型。特殊的一点是:只要类
型比 int 小(如 char、byte、short),那么在运算之前,这些值会自动地转换成 int。
例子: 
byte b1 = 12; 
byte b2 = b1 + 1; //在编译时出错了!因为 b1+1 已经是 int 型了!切记! 
 
16、浮点类型的科学表示法。在数学中 e 代表自然对数(Math.E 给出了 double 值),
而在 Java 中 e 代表 10 的幂次。浮点型的数可以这样表示 float f = 1e-27f;  代表 1
乘以 10 的负 27 次幂。 
PDF 文件使用 "pdfFactory" 试用版本创建 www.fineprint.cn
Java 语法总结 -  数组 
 
数组(array)是相同类型变量的集合,可以使用共同的名字引用它。数组可被定义为
任何类型,可以是一维或多维。数组中的一个特别要素是通过下标来访问它。数组提供
了一种将有联系的信息分组的便利方法。注意:如果你熟悉 C/C++,请注意, Java
数组的工作原理与它们不同。 
 
1、数组不是集合,它只能保存同种类型的多个原始类型或者对象的引用。数组保存的
仅仅是对象的引用,而不是对象本身。 
 
2、数组本身就是对象,Java 中对象是在堆中的,因此数组无论保存原始类型还是其他
对象类型,数组对象本身是在堆中的。 
 
3、数组声明的两种形式:一、int[] arr; 二、int arr[];  推荐使用前者,这符合 Sun
的命名规范,而且容易了解到关键点,这是一个 int 数组对象,而不是一个 int 原始类
型。 
 
4、在数组声明中包含数组长度永远是不合法的!如:int[5] arr; 。因为,声明的时候
并没有实例化任何对象,只有在实例化数组对象时,JVM 才分配空间,这时才与长度有
关。 
 
5、在数组构造的时候必须指定长度,因为JVM 要知道需要在堆上分配多少空间。反例:
int[] arr = new int[]; 
 
6、多维数组的声明。int[][][] arr; 是三维 int 型数组。 
 
7、一维数组的构造。形如:String[] sa = new String[5]; 或者分成两句:String[] 
sa;  sa = new String[5]; 
 
8、原始类型数组元素的默认值。对于原始类型数组,在用new 构造完成而没有初始化
时,JVM 自动对其进行初始化。默认值:byte、short、 int 、long--0  float--0.0f 
double--0.0  boolean--false  char--'"u0000' 。(无论该数组是成员变量还是局部
变量) 
 
9、对象类型数组中的引用被默认初始化为 null。如:Car[] myCar = new Car[10]; 
相当于从 myCar[0]到 myCar[9]都这样被自动初始化为 myCar[i] = null; 
 
10、对象类型的数组虽然被默认初始化了,但是并没有调用其构造函数。也就是说:
Car[] myCar = new Car[10]; 只创建了一个 myCar 数组对象!并没有创建 Car 对
象的任何实例! 
 
11、多维数组的构造。float[][] ratings = new float[9][]; 第一维的长度必须给出,
其余的可以不写,因为 JVM 只需要知道赋给变量 ratings 的对象的长度。 
 
PDF 文件使用 "pdfFactory" 试用版本创建 www.fineprint.cn
12、数组索引的范围。数组中各个元素的索引是从 0 开始的,到 length-1。每个数组
对象都有一个 length 属性,它保存了该数组对象的长度。(注意和 String 对象的
length()方法区分开来,这两者没有统一起来是很遗憾的。) 
 
13 、 Java 有 数 组 下 标 检 查 , 当 访问 超 出 索 引 范 围 时 , 将 产 生
ArrayIndexOutOfBoundsException 运行时异常。注意,这种下标检查不是在编译
时刻进行的,而是在运行时!也就是说 int[] arr = new int[10];  arr[100] = 100; 
这么明显的错误可以通过编译,但在运行时抛出!Java 的数组下标检查是需要额外开
销的,但是出于安全的权衡还是值得的,因为很多语言在使用数组时是不安全的,可以
任意访问自身内存块外的数组,编译运行都不会报错,产生难以预料的后果! 
PDF 文件使用 "pdfFactory" 试用版本创建 www.fineprint.cn
Java 语法总结 -  字符串 
 
Java 的 String 太特别了,也太常用了,所以重要。我初学 Java 就被它搞蒙了,太多
混淆的概念了,比如它的不变性。所以必须深入机制地去理解它。 
 
 
1、String 中的每个字符都是一个 16 位的 Unicode 字符,用 Unicode 很容易表达丰
富的国际化字符集,比如很好的中文支持。甚至 Java 的标识符都可以用汉字,但是没
人会用吧(只在一本清华的《Java2 实用教程》看过)。 
 
2、判断空字符串。根据需要自己选择某个或者它们的组合 
    if ( s == null )    // 从引用的角度 
    if ( s.length() == 0 )     // 从长度判别 
    if ( s.trim().length () == 0 )     // 是否有多个空白字符 
trim()方法的作用是是移除前导和尾部的 Unicode 值小于'"u0020'的字符,并返回“修
剪”好的字符串。这种方法很常用,比如需要用户输入用户名,用户不小心加了前导或
者尾部空格,一个好的程序应该知道用户不是故意的,即使是故意的也应该智能点地处
理。 
判断空串是很常用的操作,但是 Java 类库直到 1.6 才提供了 isEmpty()方法。当且仅
当 length()  为 0  时返回 true 。 
 
3、未初始化、空串""与 null。它们是不同的概念。对未初始化的对象操作会被编译器
挡在门外;null 是一个特殊的初始化值,是一个不指向任何对象的引用,对引用为 null
的对象操作会在运行时抛出异常 NullPointerException;而空串是长度为 0 的字符串,
和别的字符串的唯一区别就是长度为 0。 
例子: 
    public class StringTest{ 
        static String s1; 
        public static void main(String[] args) { 
            String s2; 
            String s3 = ""; 
            System.out.print(s1.isEmpty());     // 运行时异常 
            System.out.print(s2.isEmpty());     // 编译出错 
            System.out.print(s3.isEmpty());     //ok !输出 true 
        } 
    } 
 
4、String 类的方法很多,在编写相关代码的时候看看 JDK 文档时有好处的,要不然
花了大量时间实现一个已经存在的方法是很不值得的,因为编写、测试、维护自己的代
码使项目的成本增加,利润减少,严重的话会导致开不出工资…… 
 
5、字符串的比较。 
Java 不 允 许 自 定 义 操 作 符 重 载 , 因 此 字 符 串 的 比 较 要 用 compareTo() 或 者 
compareToIgnoreCase()。s1.compareTo(s2),返回值大于 0 则,则前者大;等于
PDF 文件使用 "pdfFactory" 试用版本创建 www.fineprint.cn
0,一般大;小于 0,后者大。比较的依据是字符串中各个字符的 Unicode 值。 
 
6、toString()方法。 
Java 的任何对象都有 toString()方法,是从 Object 对象继承而来的。它的作用就是
让对象在输出时看起来更有意义,而不是奇怪的对象的内存地址。对测试也是很有帮助
的。 
 
7、String 对象是不变的!可以变化的是 String 对象的引用。 
String name = "ray"; 
name.concat("long");  // 字符串连接 
System.out.println(name); //输出 name,ok,还是"ray" 
name = name.concat("long");  // 把字符串对象连接的结果赋给了 name 引用 
System.out.println(name);  //输出 name,oh!,变成了"raylong" 
上述三条语句其实产生了 3 个 String 对象,"ray","long","raylong"。第 2 条语句
确实产生了"raylong"字符串,但是没有指定把该字符串的引用赋给谁,因此没有改变
name 引用。第 3 条语句根据不变性,并没有改变"ray",JVM 创建了一个新的对象,
把"ray","long"的连接赋给了 name 引用,因此引用变了,但是原对象没变。 
 
8、String 的不变性的机制显然会在 String 常量内有大量的冗余。如:"1" + "2" + "3" 
+......+ "n" 产生了 n+(n+1)个 String 对象!因此 Java 为了更有效地使用内存,JVM
留出一块特殊的内存区域,被称为“String 常量池”。对 String 多么照顾啊!当编译器
遇见 String 常量的时候,它检查该池内是否已经存在相同的 String 常量。如果找到,
就把新常量的引用指向现有的 String,不创建任何新的 String 常量对象。 
 
那么就可能出 现多个引用指向同一个 String 常量,会不会有别名的危险呢?No 
problem!String 对象的不变性可以保证不会出现别名问题!这是 String 对象与普通
对象的一点区别。 
 
乍看起来这是底层的机制,对我们编程没什么影响。而且这种机制会大幅度提高 String
的效率,实际上却不是这样。为连接 n 个字符串使用字符串连接操作时,要消耗的时间
是 n 的平方级!因为每两个字符串连接,它们的内容都要被复制。因此在处理大量的字
符串连接时,而且要求性能时,我们不要用 String,StringBuffer 是更好的选择。 
 
8、StringBuffer 类。StringBuffer 类是可变的,不会在字符串常量池中,而是在堆
中,不会留下一大堆无用的对象。而且它可将字符串缓冲区安全地用于多个线程。每个
StringBuffer 对象都有一定的容量。只要 StringBuffer 对象所包含的字符序列的长度
没有超出此容量,就无需分配新的内部缓冲区数组。如果内部缓冲区溢出,则此容量自
动增大。这个固定的容量是 16 个字符。我给这种算法起个名字叫“添饭算法”。先给你
一满碗饭,不够了再给你一满碗饭。 
例子: 
    StringBuffer sb = new StringBuffer();    // 初始容量为 16  个字符 
    sb.append("1234");    // 这是 4 个字符,那么 16 个字符的容量就足够了,没有
溢出 
    System.out.println(sb.length());    // 输出字符串长度是 4 
PDF 文件使用 "pdfFactory" 试用版本创建 www.fineprint.cn
    System.out.println(sb.capacity());    // 输出该字符串缓冲区的容量是 16 
 
    sb.append("12345678901234567");        // 这是 17 个字符,16 个字符的容
量不够了,扩容为 17+16 个字符的容量 
    System.out.println(sb.length());    // 输出字符串长度是 17 
    System.out.println(sb.capacity());    // 输出该字符串缓冲区的容量是 34 
 
    sb.append("890").reverse().insert(10,"-");     
    System.out.println(sb);        // 输出 0987654321-09876543214321 
 
字符串的长度和字符缓冲区的容量是两个概念,注意区别。 
还有串联的方式看起来是不是很酷!用返回值连接起来可以实现这种简洁和优雅。 
 
10、StringBuilder 类。  从 J2SE 5.0 提供了 StringBuilder 类,它和 StringBuffer
类是孪生兄弟,很像。它存在的价值在于:对字符串操作的效率更高。不足的是线程安
全无法保证,不保证同步。那么两者性能到底差多少呢?很多! 
请参阅:http://book.csdn.net/bookfiles/135/1001354628.shtml 
实践: 
单个线程的时候使用 StringBuilder 类,以提高效率,而且它的 API 和 StringBuffer
兼容,不需要额外的学习成本,物美价廉。多线程时使用 StringBuffer,以保证安全。 
 
11、字符串的比较。 
下面这条可能会让你晕,所以你可以选择看或者不看。它不会对你的职业生涯造成任何
影响。而且谨记一条,比较字符串要用 equals()就 ok 了!一旦用了“==”就会出现很
怪异的现象。之所以把这部分放在最后,是想节省大家的时间,因为这条又臭又长。推
荐三种人:一、没事闲着型。二、想深入地理解 Java 的字符串,即使明明知道学了也
没用。三、和我一样爱好研究“茴”字有几种写法。 
 
还是那句老话,String 太特殊了,以至于某些规则对 String 不起作用。个人感觉这种
特殊性并不好。看例子: 
例子 A: 
    String str1 = "java"; 
    String str2 = "java"; 
    System.out.print(str1==str2); 
地球上有点 Java 基础的人都知道会输出 false,因为==比较的是引用,equals 比较
的是内容。不是我忽悠大家,你们可以在自己的机子上运行一下,结果是 true!原因
很简单,String 对象被放进常量池里了,再次出现“java”字符串的时候,JVM 很兴奋
地把 str2 的引用也指向了“java”对象,它认为自己节省了内存开销。不难理解吧  呵呵 
例子 B: 
    String str1 = new String("java"); 
    String str2 = new String("java"); 
    System.out.print(str1==str2); 
看过上例的都学聪明了,这次肯定会输出 true!很不幸,JVM 并没有这么做,结果是
false。原因很简单,例子 A 中那种声明的方式确实是在 String 常量池创建“java”对象,
PDF 文件使用 "pdfFactory" 试用版本创建 www.fineprint.cn
但是一旦看到 new 关键字,JVM 会在堆中为 String 分配空间。两者声明方式貌合神
离,这也是我把“如何创建字符串对象”放到后面来讲的原因。大家要沉住气,还有一个
例子。 
例子 C: 
    String str1 = "java"; 
    String str2 = "blog"; 
    String s = str1+str2; 
    System.out.print(s=="javablog"); 
再看这个例子,很多同志不敢妄言是 true 还是 false 了吧。爱玩脑筋急转弯的人会说
是 false 吧……恭喜你,你会抢答了!把那个“吧”字去掉你就完全正确。原因很简单,
JVM 确实会对型如 String str1 = "java"; 的 String 对象放在字符串常量池里,但是
它是在编译时刻那么做的,而 String s = str1+str2;  是在运行时刻才能知道(我们
当然一眼就看穿了,可是Java 必须在运行时才知道的,人脑和电脑的结构不同),也就
是说 str1+str2 是在堆里创建的,s 引用当然不可能指向字符串常量池里的对象。没崩
溃的人继续看例子 D。 
例子 D: 
    String s1 = "java"; 
    String s2 = new String("java"); 
    System.out.print(s1.intern()==s2.intern()); 
intern()是什么东东?反正结果是 true。如果没用过这个方法,而且训练有素的程序员
会去看 JDK 文档了。简单点说就是用intern()方法就可以用“==”比较字符串的内容了。
在我看到 intern()方法到底有什么用之前,我认为它太多余了。其实我写的这一条也很
多余,intern()方法还存在诸多的问题,如效率、实现上的不统一…… 
例子 E: 
    String str1 = "java"; 
    String str2 = new String("java"); 
    System.out.print(str1.equals(str2)); 
无论在常量池还是堆中的对象,用 equals()方法比较的就是内容,就这么简单! 
PDF 文件使用 "pdfFactory" 试用版本创建 www.fineprint.cn