众所周知,Java是一门静态类型的语言,这意味着全部的变量和表达式的类型会在编译时肯定。同时,Java 仍是一门强类型的语言,所以变量的值或表达式的结果的类型都会受到限制(好比一个声明为 String
的变量不的值不多是一个数值 1
),类型之间的运算也会被限制,这有助于在编译时发现绝大多数的错误。html
在 Java中存在两种类型:基本类型和引用类型。java
PrimitiveType: {Annotation} NumericType {Annotation} boolean NumericType: IntegralType FloatingPointType IntegralType: (one of) byte short int long char FloatingPointType: (one of) float double
Java Language Specification (Java SE 8 Edition) §4.2 Primitive Types and Values
值得注意的是,基本类型的值的状态不会被共享。oracle
好比下面这个例子:code
int i = 0; int j = i; i += 1; System.out.println(j); AtomicInteger i = new AtomicInteger(0); AtomicInteger j = i; i.addAndGet(1); System.out.println(j);
上述代码将输出:orm
0 1
整数类型 (IntegralType) 包含了如下五种类型:htm
类型 | 长度 / 位 (bit) | 取值范围 |
---|---|---|
byte |
有符号 8 | -128 ~ 127 |
short |
有符号 16 | -32768 ~ 32767 |
int |
有符号 32 | -214783648 ~ 2147483647 |
long |
有符号 64 | -9223372036854775808 ~ 9223372036854775807 |
char |
无符号 16 | \u0000 ~ \uffff 等价于 0 ~ 65535 |
<
、<=
、>
、>=
、==
、!=
,其结果为 boolean
类型;数值运算符:对象
+
、-
*
、/
、%
+
、-
++
, 分为前缀自增 (++i
) 和后缀自增 (i++
)--
, 分为前缀自减 (--i
) 和后缀自减 (i--
)位移运算符:内存
<<
>>
>>>
~
&
、^
、|
? :
cast
+
这里面加号出现了好几回,包括 一元运算符、加法运算符、自增运算符、字符串拼接运算符,后三者运算符就如它们的字面意思般,很好理解。ci
那么 一元运算符 是什么意思呢?字符串
让咱们来看看下面这份代码:
static void is (short i) { System.out.println("short"); } static void is (int i) { System.out.println("int"); } static void is (long i) { System.out.println("long"); } static void main (String[] args) { short i = 5; int j = 10; is(i); is(+i); is(-i); is(j); is(+j); is(-j); }
上述代码将输出:
short int int int int int
很显然,第 17~19 行的调用执行的是参数类型为 int
的方法,然而第 20~21 行的调用执行的并非参数类型为 long
的方法。
我在 JSL § 15.15.3 Unary Plus Operator + 中并未看出一元运算符的具体影响,根据实验结果只能推测一元运算符会将低于 int
的数值类型提高到 int
类型 (你能够声明一个 byte h = 0
,is(+h)
仍然会调用参数类型为 int
的方法),并且对于 +
和 -
都是提高类型的做用,并非直觉意义上的一个升一个降。
关于这个 一元运算符 的用法其实并非不少,有下列几种:
// 若是方法接受的是 int 类型的话,仅仅用来明确这是个正数仍是负数。 func(+1); func(-1); // 输出字符的代码值时的小技巧 // 由于字符类型 char 是低于 int 的整数类型 System.out.println(+'c'); // 99
对于移位运算符之外的整数运算而言,若是两个操做数中至少有一个 long
,那么此次运算将会按 64位精度进行计算,而且其计算结果也是 long
类型,此时若是另外一个操做数不是 long
,那么会将它提高到 long
类型再计算;若是两个操做数都不是 long
,那么会按 32位精度进行计算,而且计算结果为 int
,若是任何一个操做数不是 int
, 那么都将提高到 int
类型后再计算。
整数运算符会在如下状况抛出异常:
NullPointerException
;ArithmeticException
;OutOfMemoryError
来看看规范中给出的示例代码:
class Test { public static void main (String[] args) { int i = 1000000; System.out.println(i * i); long l = i; System.out.println(l * l); System.out.println(20296 / (l - i)); } }
上述代码将输出:
-727379968 1000000000000 ArithmeticException
对于 int
来讲,1000000^2 太大了,而因为以前的运算规则,i * i
只能保存结果的低32位,十进制下也就是 -727379968
。
Java 中的浮点类型遵循 IEEE 754
标准的定义。
IEEE 754-1985 - Wikiwand
在 IEEE 754 标准中,定义了 32位精度的 float
、64位精度的 double
,还有正负数、正负0、正负无穷和特殊的 NaN
。
NaN
用于表示无效操做的结果,好比 0.0 / 0.0
(0 / 0
才适用整数运算中的 右操做数为0 的异常规则),你能够在 Float.NaN
和 Double.NaN
中找到。
NaN
是无序的,所以
NaN
,则比较运算符 (<
、<=
、>
、>=
) 都会返回 false
NaN
,则相等运算符 (==
) 返回 false
若是 x
或 y
是 NaN
,则 (x < y) == !(x >= y)
将返回 false
NaN
, 则不等式运算符 !=
将返回 true
当且仅当 x
为 NaN
时,x != x
将返回 true
<
、<=
、>
、>=
、==
、!=
,其结果为 boolean
类型;数值运算符:
+
、-
*
、/
、%
+
、-
++
, 分为前缀自增 (++i
) 和后缀自增 (i++
)--
, 分为前缀自减 (--i
) 和后缀自减 (i--
)? :
cast
+
若是一次计算中,至少有一个二元运算符的操做数是浮点类型的,那么该操做就是一个浮点运算,即便另外一个操做数是整数。
System.out.println(10 * 0.1); // 1.0
对于浮点运算而言,若是两个操做数中至少有一个 double
,那么此次运算将会按 64位精度进行计算,而且其计算结果也是 double
类型,此时若是另外一个操做数不是 double
,那么会将它提高到 double
类型再计算;若是两个操做数都不是 double
,那么会按 32位精度进行计算,而且计算结果为 float
,若是任何一个操做数不是 float
, 那么都将提高到 float
类型后再计算。
浮点运算有溢出 (overflows) 和下溢 (underflows),其中溢出将产生有符号的无穷大,而下溢则产生一个非标准化 (denormalized) 的值或是一个有符号的0。
数学上没法肯定结果的浮点运算将产生 NaN
。
全部 NaN
参与的浮点运算都会产生 NaN
。
在下列状况中,浮点运算会抛出异常:
NullPointerException
;OutOfMemoryError
。接下来看看规范中给出的示例代码:
class Test { public static void main(String[] args) { // 溢出 double d = 1e308; System.out.print("溢出产生了无穷大: "); System.out.println(d + "*10==" + d*10); // 渐变下溢 (gradual underflow) d = 1e-305 * Math.PI; System.out.print("渐变下溢: " + d + "\n "); for (int i = 0; i < 4; i++) System.out.print(" " + (d /= 100000) + "\n"); System.out.println(); // 产生 NaN System.out.print("0.0/0.0 产生的不是数字: "); d = 0.0/0.0; System.out.println(d); // 产生不精确结果的四舍五入: System.out.print("单精度下的不精确结果:"); for (int i = 0; i < 100; i++) { float z = 1.0f / i; if (z * i != 1.0f) System.out.print(" " + i); } System.out.println(); // 另外一个产生不精确结果的四舍五入: System.out.print("双精度下的不精确结果:"); for (int i = 0; i < 100; i++) { double z = 1.0 / i; if (z * i != 1.0) System.out.print(" " + i); } System.out.println(); // 转换到整数时发生的结果阶段: System.out.print("强制转换到整数: "); d = 12345.6; System.out.println((int)d + " " + (int)(-d)); } }
上述代码将输出
溢出产生了无穷大: 1.0e + 308 * 10 == Infinity 渐变下溢: 3.141592653589793E-305 3.1415926535898E-310 3.141592653E-315 3.142E-320 0.0 0.0 / 0.0 产生的不是数字:NaN 单精度下的不精确结果:0 41 47 55 61 82 83 94 97 双精度下的不精确结果:0 49 98 强制转换到整数:12345 -12345
值得注意的是,在 渐变下溢 的例子中,咱们能够看到精度逐渐丧失。
boolean
类型表示两个逻辑量,true
和 false
。
布尔运算符是:
==
和!=
(!
&
, ^
和 |
&&
和 ||
? :
+
,当给定一个String
操做数和一个 boolean
操做数时,它将把 boolean
操做符转换为一个String
( "true"
或"false"
),而后产生一个新建立的String
,其值为两个字符串的链接结果。布尔表达式决定了几种语句中的控制流:
if
语句while
语句do
语句for
语句一个boolean
表达式还决定在 ? :
运算符中使用哪一个子表达式的值做为结果 。
只有Boolean
表达式和Boolean
表达式能够在控制流程语句中使用。
经过表达式 x!=0
,能够将整数或浮点表达式 x
转换为 boolean
值,这遵循了 C 语言约定,即任何非零值为true
。
经过表达式 obj!=null
,能够将对象引用 obj
转换为boolean
值,这一样遵循了 C 语言约定(除null
以外的任何引用为true
。
参考资料: Java Language Specification (Java SE 8 Edition) § 4.2