文章目录
一、数据类型
整型
int(4字节)的取值范围时大约-20亿到20亿,如果想要表示地球居住的人口,就需要使用long类型了。在Java中整型的范围与运行Java代码的机器无关。
在C和C++中,int和long等整型的大小和目标平台相关。在8086这样的16位处理器上整数占2字节,不过在32位处理器上,整数则为4字节。类似地,在32位处理器上long值为4字节,在64位处理器上则为8字节。由于存在这些差别,这对编写跨平台程序带来了很大难度。在Java中,所有的数值类型所占据的字节数与平台无关。
浮点类型
所有的浮点数计算都遵循IEEE 754规范。具体来说,下面是用于表示溢出和出错情况的三个特殊的浮点数值。
- 正无穷大 Double.POSITIVE_INFINITY = 1.0 / 0.0
- 负无穷大 Double.NEGATIVE_INFINITY = -1.0 / 0.0
- NaN(不是一个数字) Double.NaN = 0.0d / 0.0
char类型
char类型原本用于表示单个字符,不过,现在情况已经有所变化。如今,有些Unicode字符可以用一个char值描述,另外一些Unicode字符则需要两个char值。
之所以在Java语言里可以使用char进行中文数据的保存,是因为Java使用的是unicode这种十六进制的编码,这种编码的主要特点是可以包括任意的文字内容。使得程序的开发更加简单。
二、变量与常量
变量
声明一个变量后,必须用赋值语句对变量进行显式初始化,Java编译器不允许使用未初始化的变量。
常量
在Java中,利用关键字final声明常量。关键字final表示这个变量只能被赋值一次,就不能再更改了,习惯上,常量名使用全大写。在Java中,经常希望某个常量可以在一个类的多个方法中使用,通常将这些常量称为类常量(class constant)。可以使用关键字static final设置一个类常量。
三、运算符
算数运算符
当参与运算的两个操作数都是整数时,表示整数除法,否则,表示浮点除法。需要注意,整数被0除将会产生一个异常,而浮点数被0除将会得到无穷大或NaN结果。
可移植性是Java语言的设计目标之一。无论在哪个虚拟机上运行,同一运算应该得到同样的结果。对于浮点数的算法运算,实现这样的可移植性是相当困难的。double类型使用64位存储一个数值,而有些处理器则使用80位浮点寄存器。这些寄存器增加了中间过程的计算精度。但是这个结果可能与始终使用64位计算的结果不一样。因此,Java虚拟机的最初规范规定所有的中间计算都必须进行截断。这种做法遭到了数字社区的反对。截断计算不仅可能导致溢出,而且由于截断操作需要消耗时间,所以在计算速度上实际上要比精度慢。为此,Java程序设计语言承认了最优性能与理想的可再生性之间的冲突,并给予了改进。
在默认情况下,现在虚拟机设计者允许对中间计算结果采取拓展的精度。但是,对于使用strictfp关键字标记的方法必须使用严格的浮点计算来生成可再生的结果。
数学函数
Math类提供了一些常用的三角函数
Math.sin
Math.cos
Math.tan
Math.atan
Math.atan2
Math类提供了一些方法使整数有更好的运算安全性。如果一个计算溢出,数学运算符只是悄悄地返回错误而不做任何提醒。例如,10亿乘以3的计算结果将是-1294967296,因为最大的int值也只是刚刚超过20亿。不过,如果调用Math.multiplyExact(10_0000_0000, 3),就会生成一个异常。你可以捕获这个异常或者让程序终止,而不是允许它给一个错误的结果然后悄无声息地继续运行。
a++和++a
前缀形式会先完成加1,而后缀形式会使用变量原来的值。建议不要在表达式中使用++,因为这样的代码很容易让人困惑,而且会带来烦人的bug。
四、字符串
不可变字符串
由于不能修改Java字符串中的单个字符,所以在Java文档中将String类对象称为是不可变的。如同数字3永远是3一样,字符串“Hello”永远包含字符H、e、l、l、o的代码单元序列。你不能修改这些值,不过可以修改字符串变量。让它引用另外一个字符串,这就如同可以让原本存3的数值变量改成存放4一样。
为了弄清楚具体的工作方式,可以想象将各种字符串存放在公共的存储池中。字符串变量指向存储池中相应的位置。如果复制一个字符串变量,原始字符与复制的字符串共享相同字符。
检测字符串是否相等
下面代码的输出结果是怎样的?
String a = "hello1".substring(0,5);
String b = "hello";
System.out.println(a == b);
System.out.println(a.equals(b));
如果代码是这样的呢?
String a = "hello";
String b = "hello";
System.out.println(a == b);
System.out.println(a.equals(b));
一定不要使用==运算符检测两个字符串是否相等!这个运算符只能够确定两个字符串是否存放在同一个位置上。当然,如果字符串在同一个位置上,它们必然想等。但是,完全有可能将内容相同的多个字符串副本放置在不同的位置上。
如果虚拟机始终将相同的字符串共享,就可以使用等号运算符检测是否相等。但实际上只有字符串字面量是共享的,而+或substring等操作得到的字符串并不共享。
"=="的字节码语义
查看上面程序的字节码时,会发现,在字节码层面,==的指令是
if_acmpne,我们去Java虚拟机中看此指令的含义。不过目前看来这个翻译应该是有些问题。
构建字符串
每次拼接字符串时,都会构建一个新的String对象,即耗时,又浪费空间。使用StringBuilder可以避免这个问题的发生。StringBuilder是Java5引入的。这个类的前身是StringBuffer,它的效率稍有些低。但允许采用多线程的方式添加或删除字符。如果所有字符串编辑操作都是在单个线程中执行(大部分操作是这样),则应该使用StringBuilder。这两个类的API是一样的。
五、数组
声明数组
int[] a = new int[100];
一旦创建了数组,就不能再改变它的长度。如果程序运行中需要经常拓展数组的大小,就应该使用另一种数据结构——数组列表(array list)。
可以使用下面两种形式定义一个数组变量:
int[] a;
或
int a[];
大多数java程序员喜欢使用第一种风格,因为它可以将类型与变量名清晰的分开。
Java提供了另一种初始化方式
int[] a = {1,2,3}
也可以这样初始化
int[] a = new int[]{1,2,3}
数组排序
int[] a = {1,2,3};
Arrays.sort(a);
这个方法使用了优化的快速排序算法。快速排序算法对于大多数数据集合来说都是效率比较高的。
多维数组
声明方式
int[][] a = new int[2][3];
//或者
int[][] b = {
{1,2,3},
{4,5,6}
};
如果想要快速地打印一个二维数组,可以调用
System.out.println(Arrays.deepToString(b));