一、变量和类型
变量指的是程序运行时可变的量,相当于开辟一块内存空间来保存一些数据。类型则是对变量的种类进行了划分, 不同的类型的变量具有不同的特性 。
下面的Java中的一些数据类型分类。
1.基本数据类型
Java中基本数据类型也称为简单类型或内置类型。
数据类型 | 大小/字节 | 默认值 | 大小范围 | 封装类 |
---|---|---|---|---|
byte(字节型) | 1 | 0 | -128-127 | Byte |
short(短整型) | 2 | 0 | -32768~32767 | Short |
char(字符型) | 2 | 空 | 0~65535 | Character |
int(整型) | 4 | 0 | -2147483648~2147483647 | Integer |
float(单精度浮点型) | 4 | 0 | 1.4E-45~3.4028235E38 | Float |
long(长整型) | 8 | 0 | -9223372036854775808~9223372036854775807 | Long |
double(双精度浮点型) | 8 | 0 | 4.9E-324~1.7976931348623157E308 | Double |
boolean(布尔型) | 没有规定 | false | true或false | Boolean |
2.类型转换问题
Java 作为一个强类型编程语言, 当不同类型之间的变量相互赋值的时候, 会有教严格的校验。稍不注意便会出现问题,比如下面这些场景。
(1) int 和 long/double 相互赋值
int a = 10;
long b = 20;
a = b; // 编译出错, 提示可能会损失精度.
b = a; // 编译通过.
int a = 10;
double b = 1.0;
a = b; // 编译出错, 提示可能会损失精度.
b = a; // 编译通过
结论: 不同数字类型的变量之间赋值, 表示范围更小的类型能隐式转换成范围较大的类型, 反之则不行 。
(2) int 和 boolean 相互赋值
int a = 10;
boolean b = true;
b = a; // 编译出错, 提示不兼容的类型
a = b; // 编译出错, 提示不兼容的类型
结论: int 和 boolean 是毫不相干的两种类型, 不能相互赋值。
(3)int字面值常量 给 byte 赋值
byte a = 100; // 编译通过
byte b = 256; // 编译报错, 提示 从int转换到byte可能会有损失
结论: byte 表示的数据范围是 -128 -> +127, 256 已经超过范围, 而 100 还在范围之内 。使用字面值常量赋值的时候, Java 会自动进行一些检查校验, 判定赋值是否合理 。
(4)强制类型转换
int a = 0;
double b = 10.5;
a = (int)b;
int a = 10;
boolean b = false;
b = (boolean)a; // 编译出错, 提示不兼容的类型
结论: 使用 (类型) 的方式可以将 double 类型强制转成 int. 但是
- 强制类型转换可能会导致精度丢失. 如刚才的例子中, 赋值之后, 10.5 就变成 10 了, 小数点后面的部分被忽略.
- 强制类型转换不是一定能成功, 互不相干的类型之间无法强转.
类型转换小结:
类型转换是指将一种类型更改为另一种类型的过程。如果从低精度类型向高精度数据类型转换,则永远不会溢出,并且总是成功的;而把高精度的数据类型向低精度的数据类型转换则必然会有信息丢失,有可能失败。
因此数据类型转换有两种方式:隐式转换和显示转换。(其中显示转换也称为强制类型转换)
以下是数据类型转换注意点:
-
强转的类型必须是兼容的;
-
不能对 boolean 类型进行类型转换;
-
浮点数到整数的转换是通过舍弃小数得到,而不是四舍五入;
-
如果需要把范围大的类型赋值给范围小的, 需要强制类型转换, 但是可能精度丢 。
-
将一个字面值常量进行赋值的时候, Java 会自动针对数字范围进行检查。
3.数值提升问题
先看下面这段代码:
byte a = 10;
byte b = 20;
byte c = a + b;
System.out.println(c);
// 编译报错
Test.java:5: 错误: 不兼容的类型: 从int转换到byte可能会有损失
byte c = a + b;
a和b都是byte类型,加起来赋给c这个byte类型,按道理说没什么问题,那么为什么编译会出错呢?
答案就是:由于计算机的 CPU 通常是按照 4 个字节为单位从内存中读写数据. 为了硬件上实现方便, 诸如 byte 和 short 这种低于4 个字节的类型, 会先提升成 int, 再参与计算 。
虽然 a 和 b 都是 byte, 但是计算 a + b 会先将 a 和 b 都提升成 int, 再进行计算, 得到的结果也是 int型, 这时赋给 byte类型c, 就会出现精度丢失,所以会编译会报错。
4.变量的命名规则
硬性指标:
- 一个变量名只能包含数字, 字母, 下划线
- 数字不能开头.
- 变量名是大小写敏感的. 即 num 和 Num 是两个不同的变量.
注意: 虽然语法上也允许使用中文/美元符($)命名变量, 但是 强烈 不推荐这样做
软性指标:
- 变量命名要具有描述性, 见名知意.
- 变量名不宜使用拼音(但是不绝对).
- 变量名的词性推荐使用名词.
- 变量命名推荐 小驼峰命名法, 当一个变量名由多个单词构成的时候, 除了第一个单词之外, 其他单词首字母都大写
二、运算符
1.算术运算符
- 基本四则运算符: + - * / %
- 增量赋值运算符: += -= *= /= %=
- 自增/自减运算符 ++ –
自增和自减需要注意一下:
int a = 10;
int b = ++a;
System.out.println(b);
int c = a++;
System.out.println(c) ;
结论:
- 如果不取自增运算的表达式的返回值, 则前置自增和后置自增没有区别.
- 如果取表达式的返回值, 则前置自增的返回值是自增之后的值, 后置自增的返回值是自增之前的值
2.关系运算符
关系运算符主要有六个:
== != < > <= >=
注意点: 关系运算符的表达式返回值都是 boolean 类型
3.逻辑运算符
逻辑运算符主要有三个:
&& || !
需要注意的是:
-
逻辑运算符的操作数和返回值都是 boolean .
-
&& 和 || 都遵守短路求值的规则
System.out.println(10 > 20 && 10 / 0 == 0); // 打印 false
System.out.println(10 < 20 || 10 / 0 == 0); // 打印 true
我们都知道, 计算 10 / 0 会导致程序抛出异常. 但是上面的代码却能正常运行, 说明 10 / 0 并没有真正被求值.
结论:
- 对于 && , 如果左侧表达式值为 false, 则表达式的整体的值一定是 false, 无需计算右侧表达式.
- 对于 ||, 如果左侧表达式值为 true, 则表达式的整体的值一定是 true, 无需计算右侧表达式
最后还要注意一点:
& 和 | 如果操作数为 boolean 的时候, 也表示逻辑运算 ,但是和 && 以及 || 相比, 它们不支持短路求值 。
System.out.println(10 > 20 & 10 / 0 == 0); // 程序抛出异常
System.out.println(10 < 20 | 10 / 0 == 0); // 程序抛出异常
所以一般在表示逻辑运算的时候不推荐使用&和|。
4.移位运算符
移位运算符有三个:<< >> >>>
第三个>>>是无符号右移,这些都是按照二进制位来运算。
这三个位移操作符只能用于整数类型,且是对其二进制补码进行的。
关于机器数,真值,原码,反码,和补码
- 机器数:一个数在计算机中的二进制表示形式, 叫做这个数的机器数。机器数是带符号的,在计算机用一个数的最高位存放符号, 正数为0, 负数为1。比如,十进制中的数 +3 ,计算机字长为8位,转换成二进制就00000011。如果是 -3 ,就是 10000011 。那么,这里的 00000011 和 10000011 就是机器数。
- 真值:因为第一位是符号位,所以机器数的形式值就不等于真正的数值。例如上面的有符号数 10000011,其最高位1代表负,其真正数值是 -3 而不是形式值131(10000011转换成十进制等于131)。所以,为区别起见,将带符号位的机器数对应的真正数值称为机器数的真值。
例:0000 0001的真值 = +000 0001 = +1,1000 0001的真值 = –000 0001 = –1。 - 原码:原码就是符号位加上真值的绝对值, 即用第一位表示符号, 其余位表示值。
- 反码:正数的反码是其本身,负数的反码是在其原码的基础上, 符号位不变,其余各个位取反。比如:
[+1] = [00000001]原 = [00000001]反
[-1] = [10000001]原 = [11111110]反 - 补码:正数的补码就是其本身负数的补码是在其原码的基础上, 符号位不变, 其余各位取反, 最后+1. (即在反码的基础上+1)。比如:
[+1] = [00000001]原 = [00000001]反 = [00000001]补
[-1] = [10000001]原 = [11111110]反 = [11111111]补
带符号位的移位操作规则如下:只有负数右移的时候,高位补1,其他情况的补位,都只补0。
下面以8位二进制的移位进行举例。
10的补码为00001010,左移一位变成20(00010100),右移一位变成5(00000101);
5的补码为00000101,左移一位变成10(00001010),右移一位变成2(00000010);
-99的补码为10011101,左移一位变成58(00111010),右移一位变成-50(11001110)。
无符号位的右移操作规则如下:不管正负数,高位都补0。
小结:
- 左移 1 位, 相当于原数字 * 2. 左移 N 位, 相当于原数字 * 2 的N次方;
- 右移 1 位, 相当于原数字 / 2. 右移 N 位, 相当于原数字 / 2 的N次方;
- 右移的时候最低位被舍弃,故不是精准的除以2;
- 左移有可能改变数的正负。如果左移一位的时候,符号位和数值最高位不同,则数的正负发生变化;
- 由于计算机计算移位效率高于计算乘除, 当某个代码正好乘除 2 的N次方的时候可以用移位运算代替;
- 移动负数位或者移位位数过大都没有意义 。
5.位运算符
Java 中对数据的操作的最小单位不是字节, 而是二进制位.
位运算符主要有四个: & | ~ ^
位操作表示 按二进制位运算. 计算机中都是使用二进制来表示数据的(01构成的序列), 按位运算就是在按照二进制位的每一位依次进行计算 。
- 按位与 &: 如果两个二进制位都是 1, 则结果为 1, 否则结果为 0 ;
- 按位或 |: 如果两个二进制位都是 0, 则结果为 0, 否则结果为 1 ;
- 按位取反 ~: 如果该位为 0 则转为 1, 如果该位为 1 则转为 0 ;
- 按位异或 ^: 如果两个数字的二进制位相同, 则结果为 0, 相异则结果为 1 。
注意: 当 & 和 | 的操作数为整数(int, short, long, byte) 的时候, 表示按位运算, 当操作数为 boolean 的时候, 表示逻辑
运算.
6.条件运算符
条件运算符只有一个: 表达式1 ? 表达式2 : 表达式3
当 表达式1 的值为 true 时, 整个表达式的值为 表达式2 的值; 当 表达式1的值为 false 时, 整个表达式的值为 表达式3 的值.
也是 Java 中唯一的一个 三目运算符, 是条件判断语句的简化写法 。
7.运算符的优先级
不用刻意记忆,在有歧义的地方加上括号即可。