浮点型
浮点类型的数就是小数,因为小数用迷信计数法示意的时候,小数点是能够“浮动”的,如 1234.5 能够示意成 12.345×102,也能够示意成 1.2345×103,所以称为浮点数。
float f1 = 3.14f;
float f2 = 3.14e38f; // 迷信计数法示意的 3.14x10^38
double d = 1.79e308;
double d2 = -1.79e308;
double d3 = 4.9e-324; // 迷信计数法示意的 4.9x10^-324
对于 float
类型,须要加上 f
后缀。
浮点数可示意的范畴十分大,float
类型可最大示意 3.4×1038,而 double
类型可最大示意 1.79×10308。
布尔类型
布尔类型 boolean
只有 true
和false
两个值,布尔类型总是关系运算的计算结果:
boolean b1 = true;
boolean b2 = false;
boolean isGreater = 5 > 3; // 计算结果为 true
int age = 12;
boolean isAdult = age >= 18; // 计算结果为 false
Java 语言对布尔类型的存储并没有做规定,因为实践上存储布尔类型只须要 1 bit,然而通常 JVM 外部会把 boolean
示意为 4 字节整数。
字符类型
字符类型 char
示意一个字符。Java 的 char
类型除了可示意规范的 ASCII 外,还能够示意一个 Unicode 字符:
布尔类型
布尔类型 boolean
只有 true
和false
两个值,布尔类型总是关系运算的计算结果:
boolean b1 = true;
boolean b2 = false;
boolean isGreater = 5 > 3; // 计算结果为 true
int age = 12;
boolean isAdult = age >= 18; // 计算结果为 false
Java 语言对布尔类型的存储并没有做规定,因为实践上存储布尔类型只须要 1 bit,然而通常 JVM 外部会把 boolean
示意为 4 字节整数。
字符类型
字符类型 char
示意一个字符。Java 的 char
类型除了可示意规范的 ASCII 外,还能够示意一个 Unicode 字符:
public class Main {public static void main(String[] args) {
char a = 'A';
char zh = '中';
System.out.println(a);
System.out.println(zh);
}
}
留神 char
类型应用单引号 '
,且仅有一个字符,要和双引号"
的字符串类型区离开。
援用类型
除了上述根本类型的变量,剩下的都是援用类型。例如,援用类型最罕用的就是 String
字符串:
String s = "hello";
援用类型的变量相似于 C 语言的指针,它外部存储一个“地址”,指向某个对象在内存的地位,后续咱们介绍类的概念时会具体探讨。
常量
定义变量的时候,如果加上 final
修饰符,这个变量就变成了常量:
final double PI = 3.14; // PI 是一个常量
double r = 5.0;
double area = PI * r * r;
PI = 300; // compile error!
常量在定义时进行初始化后就不可再次赋值,再次赋值会导致编译谬误。
常量的作用是用有意义的变量名来防止魔术数字(Magic number),例如,不要在代码中到处写3.14
,而是定义一个常量。如果未来须要进步计算精度,咱们只须要在常量的定义处批改,例如,改成3.1416
,而不用在所有中央替换3.14
。
依据习惯,常量名通常全副大写。
var 关键字
有些时候,类型的名字太长,写起来比拟麻烦。例如:
StringBuilder sb = new StringBuilder();
这个时候,如果想省略变量类型,能够应用 var
关键字:
var sb = new StringBuilder();
编译器会依据赋值语句主动推断出变量 sb
的类型是StringBuilder
。对编译器来说,语句:
var sb = new StringBuilder();
实际上会主动变成:
StringBuilder sb = new StringBuilder();
因而,应用 var
定义变量,仅仅是少写了变量类型而已。
变量的作用范畴
在 Java 中,多行语句用 {} 括起来。很多管制语句,例如条件判断和循环,都以 {} 作为它们本身的范畴,例如:
if (...) { // if 开始
...
while (...) { while 开始
...
if (...) { // if 开始
...
} // if 完结
...
} // while 完结
...
} // if 完结
只有正确地嵌套这些{},编译器就能辨认出语句块的开始和完结。而在语句块中定义的变量,它有一个作用域,就是从定义处开始,到语句块完结。超出了作用域援用这些变量,编译器会报错。举个例子:
{
...
int i = 0; // 变量 i 从这里开始定义
...
{
...
int x = 1; // 变量 x 从这里开始定义
...
{
...
String s = "hello"; // 变量 s 从这里开始定义
...
} // 变量 s 作用域到此结束
...
// 留神,这是一个新的变量 s,它和下面的变量同名,// 然而因为作用域不同,它们是两个不同的变量:
String s = "hi";
...
} // 变量 x 和 s 作用域到此结束
...
} // 变量 i 作用域到此结束
定义变量时,要遵循作用域最小化准则,尽量将变量定义在尽可能小的作用域,并且,不要重复使用变量名。
整数运算
public class Main {public static void main(String[] args) {int i = (100 + 200) * (99 - 88); // 3300
int n = 7 * (5 + (i - 9)); // 23072
System.out.println(i);
System.out.println(n);
}
}
int y = 12345 % 67; // 12345÷67 的余数是 17
溢出
要特地留神,整数因为存在范畴限度,如果计算结果超出了范畴,就会产生溢出,而溢出_不会出错_,却会失去一个奇怪的后果:
public class Main {public static void main(String[] args) {
int x = 2147483640;
int y = 15;
int sum = x + y;
System.out.println(sum); // -2147483641
}
}
要解释上述后果,咱们把整数 2147483640
和15
换成二进制做加法:
0111 1111 1111 1111 1111 1111 1111 1000
+ 0000 0000 0000 0000 0000 0000 0000 1111
-----------------------------------------
1000 0000 0000 0000 0000 0000 0000 0111
因为最高位计算结果为1
,因而,加法后果变成了一个正数。
要解决下面的问题,能够把 int
换成 long
类型,因为 long
可示意的整型范畴更大,所以后果就不会溢出
移位运算
在计算机中,整数总是以二进制的模式示意。例如,int
类型的整数 7
应用 4 字节示意的二进制如下:
00000000 0000000 0000000 00000111
能够对整数进行移位运算。对整数 7
左移 1 位将失去整数14
,左移两位将失去整数28
:
int n = 7; // 00000000 00000000 00000000 00000111 = 7
int a = n << 1; // 00000000 00000000 00000000 00001110 = 14
int b = n << 2; // 00000000 00000000 00000000 00011100 = 28
int c = n << 28; // 01110000 00000000 00000000 00000000 = 1879048192
int d = n << 29; // 11100000 00000000 00000000 00000000 = -536870912
左移 29 位时,因为最高位变成1
,因而后果变成了正数。
相似的,对整数 28 进行右移,后果如下:
int n = 7; // 00000000 00000000 00000000 00000111 = 7
int a = n >> 1; // 00000000 00000000 00000000 00000011 = 3
int b = n >> 2; // 00000000 00000000 00000000 00000001 = 1
int c = n >> 3; // 00000000 00000000 00000000 00000000 = 0
如果对一个正数进行右移,最高位的 1
不动,后果依然是一个正数:
int n = -536870912;
int a = n >> 1; // 11110000 00000000 00000000 00000000 = -268435456
int b = n >> 2; // 11111000 00000000 00000000 00000000 = -134217728
int c = n >> 28; // 11111111 11111111 11111111 11111110 = -2
int d = n >> 29; // 11111111 11111111 11111111 11111111 = -1
还有一种无符号的右移运算,应用 >>>
,它的特点是不论符号位,右移后高位总是补0
,因而,对一个正数进行>>>
右移,它会变成负数,起因是最高位的 1
变成了0
:
int n = -536870912;
int a = n >>> 1; // 01110000 00000000 00000000 00000000 = 1879048192
int b = n >>> 2; // 00111000 00000000 00000000 00000000 = 939524096
int c = n >>> 29; // 00000000 00000000 00000000 00000111 = 7
int d = n >>> 31; // 00000000 00000000 00000000 00000001 = 1
对 byte
和short
类型进行移位时,会首先转换为 int
再进行位移。
仔细观察可发现,左移实际上就是一直地×2,右移实际上就是一直地÷2。
位运算
位运算是按位进行与、或、非和异或的运算。
与运算的规定是,必须两个数同时为1
,后果才为1
:
n = 0 & 0; // 0
n = 0 & 1; // 0
n = 1 & 0; // 0
n = 1 & 1; // 1
或运算的规定是,只有任意一个为1
,后果就为1
:
n = 0 | 0; // 0
n = 0 | 1; // 1
n = 1 | 0; // 1
n = 1 | 1; // 1
非运算的规定是,0
和 1
调换:
n = ~0; // 1
n = ~1; // 0
异或运算的规定是,如果两个数不同,后果为1
,否则为0
:
n = 0 ^ 0; // 0
n = 0 ^ 1; // 1
n = 1 ^ 0; // 1
n = 1 ^ 1; // 0
对两个整数进行位运算,实际上就是按位对齐,而后顺次对每一位进行运算。例如:
public class Main {public static void main(String[] args) {
int i = 167776589; // 00001010 00000000 00010001 01001101
int n = 167776512; // 00001010 00000000 00010001 00000000
System.out.println(i & n); // 167776512
}
}
类型主动晋升与强制转型
在运算过程中,如果参加运算的两个数类型不统一,那么计算结果为较大类型的整型。例如,short
和 int
计算,后果总是 int
,起因是short
首先主动被转型为int
:
public class Main {public static void main(String[] args) {
short s = 1234;
int i = 123456;
int x = s + i; // s 主动转型为 int
short y = s + i; // 编译谬误!
}
}
也能够将后果强制转型,行将大范畴的整数转型为小范畴的整数。强制转型应用 (类型)
,例如,将int
强制转型为short
:
int i = 12345;
short s = (short) i; // 12345
要留神,超出范围的强制转型会失去谬误的后果,起因是转型时,int
的两个高位字节间接被扔掉,仅保留了低位的两个字节
浮点数运算
浮点数运算和整数运算相比,只能进行加减乘除这些数值计算,不能做位运算和移位运算。
在计算机中,浮点数尽管示意的范畴大,然而,浮点数有个十分重要的特点,就是浮点数经常无奈准确示意。
举个栗子:
浮点数 0.1
在计算机中就无奈准确示意,因为十进制的 0.1
换算成二进制是一个有限循环小数,很显然,无论应用 float
还是 double
,都只能存储一个0.1
的近似值。然而,0.5
这个浮点数又能够准确地示意。
因为浮点数经常无奈准确示意,因而,浮点数运算会产生误差:
因为浮点数存在运算误差,所以比拟两个浮点数是否相等经常会呈现谬误的后果。正确的比拟办法是判断两个浮点数之差的绝对值是否小于一个很小的数:
// 比拟 x 和 y 是否相等,先计算其差的绝对值:
double r = Math.abs(x - y);
// 再判断绝对值是否足够小:
if (r < 0.00001) {// 能够认为相等} else {// 不相等}
浮点数在内存的示意办法和整数比更加简单。
类型晋升
如果参加运算的两个数其中一个是整型,那么整型能够主动晋升到浮点型:
public class Main {public static void main(String[] args) {
int n = 5;
double d = 1.2 + 24.0 / n; //1.2+5.8 = 6.0
System.out.println(d);
}
}
须要特地留神,在一个简单的四则运算中,两个整数的运算不会呈现主动晋升的状况。例如:
double d = 1.2 + 24 / 5; // 5.2
计算结果为 5.2
,起因是编译器计算24 / 5
这个子表达式时,按两个整数进行运算,后果仍为整数4
。
溢出
整数运算在除数为 0
时会报错,而浮点数运算在除数为 0
时,不会报错,但会返回几个非凡值:
NaN
示意 Not a NumberInfinity
示意无穷大-Infinity
示意负无穷大
例如:
double d1 = 0.0 / 0; // NaN
double d2 = 1.0 / 0; // Infinity
double d3 = -1.0 / 0; // -Infinity
这三种非凡值在理论运算中很少碰到,咱们只须要理解即可。
强制转型
能够将浮点数强制转型为整数。在转型时,浮点数的小数局部会被丢掉。如果转型后超过了整型能示意的最大范畴,将返回整型的最大值。例如:
int n1 = (int) 12.3; // 12
int n2 = (int) 12.7; // 12
int n2 = (int) -12.7; // -12
int n3 = (int) (12.7 + 0.5); // 13
int n4 = (int) 1.2e20; // 2147483647
如果要进行四舍五入,能够对浮点数加上 0.5 再强制转型~~~~
布尔运算
boolean isGreater = 5 > 3; // true
int age = 12;
boolean isZero = age == 0; // false
boolean isNonZero = !isZero; // true
boolean isAdult = age >= 18; // false
boolean isTeenager = age >6 && age <18; // true
短路运算
布尔运算的一个重要特点是短路运算。如果一个布尔运算的表达式能提前确定后果,则后续的计算不再执行,间接返回后果。
因为 false && x
的后果总是 false
,无论x
是true
还是 false
,因而,与运算在确定第一个值为false
后,不再持续计算,而是间接返回false
。
三元运算符
Java 还提供一个三元运算符b ? x : y
,它依据第一个布尔表达式的后果,别离返回后续两个表达式之一的计算结果。
字符和字符串
在 Java 中,字符和字符串是两个不同的类型。