1.基本数据类型
类型 | 位 | 字节 | 特点 | 数据范围 |
---|---|---|---|---|
byte | 8 | 1 | -2^7 ~ 2^7,第一位表示正负 | |
char | 16 | 2 | 0 ~ 2^16-1 | |
short | 16 | 2 | -2^15 ~ 2^15-1,第一位表示正负 | |
int | 32 | 4 | java 中整数的默认类型 | -2^31 ~ 2^31-1,第一位表示正负 |
long | 64 | 8 | 在数字末尾加上 L标识为 long 类型 | -2^63 ~ 2^63-1,第一位表示正负 |
float | 32 | 4 | 需要在数字末尾加上 F / f | -2^31 ~ 2^31-1,第一位表示正负 |
double | 64 | 8 | java 中浮点数的默认类型,在数字末尾加上 D 标识为 double 类型 | -2^63 ~ 2^63-1,第一位表示正负 |
boolean | 没有给出精确的定义 | 没有给出精确的定义 |
1.1基本数据类型的级别
-
整型数中 byte < short < int < long
-
浮点数中 float < double
java 基本数据运算时,如果涉及"低级别"数据类型与"高级别数据类型"运算,得出的结果自动向"高级别数据类型"转型。
读者可以通过 1.2 中的运算练习理解 ↓
1.2基本数据类型的运算
1.2.1 char
//practice 以下题目是否编译成功
char c1 = 97;
char c2 = 97;
1.c1 = c1 + 1;
2.System.out.println(c1);
//output:
1.编译不通过,char 类型与 int 类型相加,结果为 int 类型(自动向上转型),而char 为2字节,无法装下 4 字节的数据,需要将运算结果强转为 char 类型(强制向下转型) :c1 = (char) (c1 + 1); 或者用 int 类型的变量去接收运算结果:int i = c1 + 1;
2.b // ASCII 表中 97 对应 'a'
1.2.2byte
//practice 以下题目是否编译成功
byte b1 = 3;
byte b2 = 4;
1. byte b3 = b1 + b2;
2. byte b4 = 3 + 4;
3. b3 += b1;
byte b5 = 'a';
byte b6 = 'a';
4. byte b7 = b5 + b6;
5. System.out.println(b7);
6. byte b8 = 128;
//output
1.编译不成功,自动向上转型为 int,需要强转
2.编译成功
3.编译成功,原理见下方。1.3赋值运算符
4.编译不成功,自动向上转型为 int,需要强转
5.输出为 194,因为 byte 是整数类型,'a' 在 ASCII 表中对应数字 97
6.编译不成功,超过数据范围
1.2.3 short
//practice 以下题目是否编译成功
1.short s1 = 'a';
short s2 = 2;
2.short s0 = 'ab';
3.short s5 = '你';
4.short s3 = s1 + s2;
5.short s4 = 3 + 4;
6.s3 += s2;
short s6;
成员变量s5;(未通过代码赋值)
7.s5 += 3;
8.s6 += 3;
//output
1.编译成功
2.编译不成功,虽然short是两字节,但是java中单引号只能包含一个字符。
3.编译通过,中文字符占两个字节
4.编译不成功,自动向上转型为 int
5.编译成功
6.编译成功,**原理见下方,1.3赋值运算符**
7.编译成功,成员变量在类加载过程中已赋值
8.编译不成功,s6暂未初始化
1.2.4 float
//practice 以下题目是否编译成功
float f1 = 1.0F;
float f2 = 2.0F;
float f3 = 2.0; //编译不通过,浮点数默认为 double 类型,应在数字末尾加上F,表示强转
1.float f4 = f1 + f2;
2.float f5 = 1.0 + 2.0;
3.float f6 = 1.0F + 2.0;
//output
1.编译通过,f1,f2已经声明为 float 类型,两个 float 类型相加还未 float 类型
2.编译不通过,1.0与2.0,末尾没有加上f,在 Java 中默认浮点数为 double 类型,double 类型相加为 double类型,不能直接将 double 类型值赋值给 float 类型变量。
3.编译不通过,2.0 为 double 类型,与 1.0F 相加自动向上转型为 double 类型。
1.3比较运算符
- ‘>’ 2. ‘<’ 3.’==’ 4. ‘<=’ 5. ’ >=’ 6.’!=’
1.4赋值运算符
= ,+=, -=, *=, /=
除了 = ,其他都是特殊的赋值运算符,通过下面的例子可以理解其含义
int i1 = 100;
i1 += 1.0f; //编译成功
第二行代码相当于 i1 = (int) (100 + 1.0f);
特殊的赋值运算符,计算完成后会对结果进行相应的类型转换,再将结果赋值给变量。
1.5自增,自减运算符
在 Java 中,不可再分的操作称为原子操作。而自增,自减 是非原子操作。
例:i++。JVM 将自增操作分解为四个原子操作
// javap反编译 i++,结果如下
getfield 取出 i 的值
iconst_1 将常量 1 压栈,为运算做准备
iadd 运算 i + 1
putfield 将运算结果赋值给 i
1.6逻辑运算符
- && :并且
(condition1 && condition2 ) 当两个条件都为 true 时,返回 true。如果其中至少一个条件为 false ,则返回 false。
&& 具有短路效果,当靠左的条件为 false 时已经足够确定结果,那么靠右的条件不会再进行判断。从而节省一定的性能
- || :或者
(condition1 || condition2 ) 当两个条件都为 false,才返回 false。其他结果返回 true。
&& 具有短路效果,当靠左的条件为 true 时已经足够确定结果,那么靠右的条件不会再进行判断。从而节省一定的性能
- ! :非
(! condition1 ) 当条件为 false 时,返回 true。反之返回 false。
1.7位运算符
位运算符只针对整型进行计算,而且位运算优先级最低
- ‘&’ :按位相与,两个二进制数如果相对应位都是1,则结果为1,否则为0。
num1 & num2 = num3
num3 一定小于等于num1,num2
Eg:
00001110
& 00000101
————————————
00000100
在 Java 中,通过 ‘&’ 运算符计算数组下标随处可见。例如在 HashMap 的 putVal() 方法中:
if ((p = tab[i = (n - 1) & hash]) == null)
tab[i] = newNode(hash, key, value, null);
n - 1 是数组下标最大值,hash 是新元素的散列值。
(n - 1) & hash 当这两个值按位相与,只能得出最大不大于 n-1 的数字,避免了数组越界的可能。
- ‘|’:按位或,两个二进制数如果相对应位都是0,则结果为0,否则为1。
Eg:
00001110
| 00000101
————————————
00001111
- ‘^’:按位异或,如果相对应位值相同,则结果为0,否则为1
Eg:
00001110
^ 00000101
————————————
00001011
- ‘~’:取反,按位取反运算符翻转操作数的每一位,即0变成1,1变成0
int a = 10;
int b = ~a;
a: 1010
b: 0101
练习:通过位运算实现使用加法做减法
//补码 = 反码 + 1,补码表示相反数
int i = 10;
System.out.println(Integer.toBinaryString(i));
int ii = 7;
System.out.println(Integer.toBinaryString(ii));
System.out.println(Integer.toBinaryString(~ii));
System.out.println(Integer.toBinaryString(~ii+1));
int iii = i + (~ii+1); //原码 + 补码
System.out.println(Integer.toBinaryString(iii));
System.out.println(iii);
//output
1010
111
11111111111111111111111111111000
11111111111111111111111111111001
11
3
- ’<<‘:逻辑左移
num << n; 相当于 num * (2^n)
左移一位,相当于 * 2。左移 n 位 * 2^n
在 Java 中,经常使用 i + j << 1,代替 (i + j) * 2
i + j << 1 中 i+j 不用加括号,因为位运算默认优先级最低
- ‘>>’:算术右移,符号位不变
num >> n; 相当于 num / (2^n)
右移一位,相当于 / 2。右移n 位: / 2^n
在 Java 中,经常使用 i + j >> 1,代替 (i + j) / 2
i + j >> 1 中 i+j 不用加括号,因为位运算默认优先级最低
Eg:ArrayList 中的扩容机制:grow() 方法
int newCapacity = oldCapacity + (oldCapacity >> 1);
新容量为旧容量的 1.5 倍
- ‘>>>’:逻辑右移,当整型数为正数时,与算术右移相同
int bit1 = 2;
int bit2 = -8;
System.out.println(bit1 + bit2 >> 1);
System.out.println(bit1 + bit2 >>> 1);
System.out.println(Integer.toBinaryString(-6));
System.out.println(Integer.toBinaryString(bit1 + bit2 >>> 1));
System.out.println(Integer.toBinaryString(Integer.MAX_VALUE));
//output
-3
2147483645
11111111111111111111111111111010
1111111111111111111111111111101 //"31位+符号为为0" -> 01111111111111111111111111111101
1111111111111111111111111111111 //"31位+符号为为0" -> 01111111111111111111111111111111
由以上例子可以看出,’>>>’ 右移时,符号位也会右移,如果原来是负数符号位为 1,右移后符号位为0
1.8三元运算符
(condition) ?表达式1:表达式2;
当 condition 为 true 时,执行表达式1;当 condition 为false 时,执行表达式2