数据类型转换
基本数据类型变量的转换不涉及
boolean
型。 通常,字符串不能直接转换为基本类型,但通过基本类型对应的包装类则可以实现把字符串转换成基本类型。
自动类型提升
处于较小表数范围类型的常量数据赋值给较大表数范围类型的变量时,将发生自动类型提升。
表数范围从小到大的自动类型提升是“隐式”进行的。
<范围大的类型> <范围大的变量名> = <原本范围小的数据>;
示例:
long num1 = 100;
、double num2 = 3.14F;
。
多种类型的数据混合运算时,首先将自动把所有数据转换成当前类型中容量最大的类型,然后计算。
由于 byte、short、char 类型数据彼此计算后的结果在同样数据类型水平上可能会发生溢出。为了安全,在计算时先自动提升为 int 类型并将结果以 int 类型返回。
当把任何基本数据类型的值和字符串(
String
)进行连接运算(+
)时,基本数据类型的值将自动转化为字符串(String
)类型。
有 byte 或 short 类型参与的自动类型提升
class ByteShort2Int {
public static void main(String[] args) {
byte byte1 = 5;
byte byte2 = 5;
int result1 = byte1 + byte2;
System.out.println("result1 = " + result1);
short short1 = 1;
short result2 = (short) (short1 + byte1);
System.out.println("result2 = " + result2);
}
}
运行结果:
result1 = 10
result2 = 6
有 short 类型参与的自动类型提升
class Char2Int {
public static void main(String[] args) {
char character = 'A';
System.out.println(character + 1);
}
}
运行结果:
66
强制类型转换
表数范围从大到小地强制类型转换需要“显式”地进行。强制类型转换是自动类型提升的逆过程。
直接(隐式地)转换将在编译时出现“不兼容的类型”错误,应带上强制转换符((<datatype>)
)操作:<范围小的类型> <范围小的变量名> = (<范围小的类型>) <原本范围大的数据>;
谨慎使用强制类型转换(转换过程中的截断操作可能造成数据溢出或精度损失)
整数的数据溢出
class DataOverflow {
public static void main(String[] args) {
int totalPopulation = (int) 6000000000L;
System.out.println("totalPopulation = " + totalPopulation);
}
}
int 正整数部分的最大表数范围在 21 亿左右,60 亿(long 型)的全球人口数据在强制类型转换时会发生数据溢出:
totalPopulation = 1705032704
浮点数的精度损失
class PrecisionLoss {
public static void main(String[] args) {
int num = (int) 3.99;
System.out.println("num = " + num);
}
}
double 型的 3.99 在强制类型转换时,损失了小数位的精度,只截取到了整数部分(区别于“四舍五入”):
num = 3
基本数据类型与引用数据类型 String 间的转换
- String 属于引用数据类型;
String 可以和包括 boolean 型在内的 8 种基本数据类型变量参与连接运算(
+
),返回的运算结果是 String 类型。class StringTest{
public static void main(String[] args){
char c = 'a'; //字母a的Unicode编码值是97
int num = 10;
String str = "hello";
System.out.println(c + num + str);
System.out.println(c + str + num);
System.out.println(c + (num + str));
System.out.println((c + num) + str);
System.out.println(str + num + c);
System.out.println();
System.out.println("* *");
System.out.println('*' + '\t' + '*');
System.out.println('*' + "\t" + '*');
System.out.println('*' + '\t' + "*");
System.out.println('*' + ('\t' + "*"));
}
}
运行结果: ``` 107hello ahello10 a10hello 107hello hello10a
- * 93
- 51
- *
```
运算符(operator)
运算符 (operator) 是一种特殊的符号,用以表示数据的运算、赋值和比较等。算术运算符
| 运算符 | 运算 | 范例 | 结果 | | —- | —- | —- | —- | |+
| 正 |+3
| 3 | |-
| 负 |b = 4; -b
| -4 | |+
| 加 |5 + 5
| 10 | |-
| 减 |6 - 4
| 2 | |*
| 乘 |3 * 4
| 12 | |/
| 除 |5 / 5
| 1 | |%
| 取余/模 |7 % 5
| 2 | |++
| 自增(前):先运算后取值
自增(后):先取值后运算 |a = 2; b = ++a;
a = 2; b = a++;
| a=3; b=3
a=3; b=2 | |--
| 自减(前):先运算后取值
自减(后):先取值后运算 |a = 2; b = --a
a = 2; b = a--
| a=1; b=1
a=1; b=2 | |+
| 字符串连接 |"He" + "llo"
| “Hello” |
class AriTest{
public static void main(String[] args){
int num1 = 12;
int num2 = 5;
// [/]
int result1 = num1 / num2;
int result2 = num1 / num2 * num2;
double result3 = num1 / num2;
double result4 = num1 / (num2 + 0.0);
double result5 = num1 / num2 + 0.0;
double result6 = (double)num1 / num2;
double result7 = (double)(num1 / num2);
System.out.println(result1); // 2
System.out.println(result2); // 10
System.out.println(result3); // 2.0
System.out.println(result4); // 2.4
System.out.println(result5); // 2.0
System.out.println(result6); // 2.4
System.out.println(result7); // 2.0
// [%](取余后的符号与被余数的符号相同)
int m1 = 12; int n1 = 5;
int m2 = -12; int n2 = 5;
int m3 = 12; int n3 = -5;
int m4 = -12; int n4 = -5;
System.out.println("m1 % n1 = " + m1 % n1); // m1 % n1 = 2
System.out.println("m2 % n2 = " + m2 % n2); // m2 % n2 = -2
System.out.println("m1 % n1 = " + m3 % n3); // m1 % n1 = 2
System.out.println("m4 % n4 = " + m4 % n4); // m4 % n4 = -2
//(前)++:先自增1,然后运算, (前)--:先自减法1,然后运算;
//(后)++:先运算,然后自增1,(后)--:先运算,然后自减增1。
int a1 = 10; int b1 = ++a1;
int a2 = 10; int b2 = a2++;
System.out.println("a1 = " + a1 + "; b1 = " + b1); // a1 = 11; b1 = 11
System.out.println("a2 = " + a2 + "; b2 = " + b2); // a2 = 11; b2 = 10
int a3 = 10;
++a3; // 和 a3++ 等价
System.out.println(a3); // 11
//注意1:自增1的结果不会改变变量原始的数据类型
short s1 = 10;
// s1 = s1 + 1; // cannot convert from int to short
s1++; // 结果与 s1 = (short)(s1 + 1); 相同,但效率更高
System.out.println(s1); // 11
//注意2:
byte bb1 = -128;
bb1--;
System.out.println(bb1); // 127
}
}
给出一个 3 位整数,输出它的个、十、百位:
class AriExer{
public static void main(String[] args){
int num = 587;
int individual = num % 10;
int ten = num % 100 / 10;
int hundred = num / 100;
System.out.println("百位:" + hundred + ";十位:" + ten + ";个位:" + individual + '。');
}
}
运行结果:
百位:5;十位:8;个位:7。
赋值运算符(=
)
当=
两侧数据类型不一致时,可以使用自动类型转换或强制类型转换原则进行处理。
支持连续赋值; 扩展赋值运算符(不会改变变量的数据类型):
+=
,-=
,*=
,/=
。
class AriTest{
public static void main(String[] args){
int n = 10;
n += (n++) + (++n); // 等价于 n = n + (n++) + (++n)。即,n = 10 + 10 + (11 + 1)
System.out.println(n); // 32
}
}
比较运算符(关系运算符)
运算符 | 运算 | 范例 | 结果 |
---|---|---|---|
== |
等于 | 4 == 3 |
false |
!= |
不等于 | 4 != 3 |
true |
< |
小于 | 4 < 3 |
false |
> |
大于 | 4 > 3 |
true |
<= |
小于或等于 | 4 <= 3 |
false |
>= |
大于或等于 | 4 >= 3 |
true |
instanceof |
是否是类的实例 | "Hello" instanceof String |
true |
比较运算符的结果都是 boolean 型——要么是 true,要么是 false。
class AriTest{
public static void main(String[] args){
int i = 10;
int j = 20;
System.out.println(i == j);
System.out.println(i = j);
}
}
运行结果:
false
20
逻辑运算符
a | b | **a & b** |
**a && b** |
**a | b** |
**a || b** |
**! a** |
**a ^ b** |
---|---|---|---|---|---|---|---|
true | true | true | true | true | true | false | false |
true | false | false | false | true | true | false | true |
false | true | false | false | true | true | true | true |
false | false | false | false | false | false | true | false |
逻辑与 一假俱假 |
短路与 |
| 逻辑或
一真俱真 | 短路或
| 逻辑非
真假相反 | 逻辑异或
同假异真 |
a & b
与a && b
异同的比较:
class LogicTest{
public static void main(String[] args){
boolean b1 = true;
b1 = false; // 同时注释或执行将出现不同结果
int num1 = 10;
if(b1 & (num1++ > 0)){ // 逻辑与
System.out.println("我在这里!");
}else{
System.out.println("I am here!");
}
System.out.println("num1 = " + num1);
boolean b2 = true;
b2 = false; // 同时注释或执行将出现不同结果
int num2 = 10;
if(b2 && (num2++ > 0)){ // 短路与
System.out.println("我在这里!");
}else{
System.out.println("I am here!");
}
System.out.println("num2 = " + num2);
}
}
同时未注释第 4 和 14 行时的运行结果:
I am here!
num1 = 11
I am here!
num2 = 10
同时注释第 4 和 14 行时的运行结果:
我在这里!
num1 = 11
我在这里!
num2 = 11
解释:
逻辑运算式被逻辑与 或短路与 连接时:
- 逻辑与:即使左侧逻辑为
false
,右侧的逻辑依旧执行;- 短路与:一旦左侧逻辑为
false
,右侧的逻辑跳出执行(短路)。但运算结果与选择使用
&
或&&
无关。
a | b
与a || b
的异同比较:
class LogicTest{
public static void main(String[] args){
boolean b1 = true;
b1 = false; // 同时注释或执行将出现不同结果
int num1 = 10;
if(b1 | (num1++ > 0)){ // 逻辑或
System.out.println("我在这里!");
}else{
System.out.println("I am here!");
}
System.out.println("num1 = " + num1);
boolean b2 = true;
b2 = false; // 同时注释或执行将出现不同结果
int num2 = 10;
if(b2 || (num2++ > 0)){ // 短路或
System.out.println("我在这里!");
}else{
System.out.println("I am here!");
}
System.out.println("num2 = " + num2);
}
}
同时未注释第 4 和 14 行时的运行结果:
我在这里!
num1 = 11
我在这里!
num2 = 11
同时注释第 4 和 14 行时的运行结果:
我在这里!
num1 = 11
我在这里!
num2 = 10
解释:
逻辑运算式被逻辑或 或短路或 连接时:
- 逻辑或:逻辑总是完整执行;
- 短路或:一旦左侧逻辑为
true
,右侧的逻辑跳出执行(短路)。但运算结果与选择使用
|
或||
无关。
建议:
从执行效率考虑,优先使用短路与(
&&
)和短路或(||
)。
class IfTest{
public static void main(String[] args){
boolean x = true, y = false;
short z = 40;
if((z++ == 40) && (y = true)){
z++;
}
if((x = false) || (++z == 43)){
z++;
}
System.out.println("z = " + z);
}
}
运行结果:
44
位运算符
限于对整型数据的操作。
运算符 | 运算 | 细节 | 范例 |
---|---|---|---|
<< |
左移 | 将N在二进制水平左移 n 位(拿 0 补): N << n 等价于:N×2n |
3 << 2 = 12 ↓ 3×22 = 12 |
>> |
右移 | 将N在二进制水平右移 n 位(拿高位补): N >> n 等价于:N×2-n |
3 >> 1 = 1 ↓ 3/2 = 1 |
>>> |
无符号右移 | 被移位二进制最高位无论是 0 或 1,都用 0 补 | 3 >>> 1 = 1 ↓ 3/2 = 1 |
& |
与运算 | 二进制位进行 & 运算:只有1&1时结果是1,否则是0 | 6 & 3 = 2 |
| |
或运算 | 二进制位进行 & 运算:只有 0|0 时结果是 0,否则是 1 | 6 | 3 = 7 |
^ |
异或运算 | 二进制位进行 ^ 运算:相同二进制位结果是 0,不同二进制位结果是 1 | 6 ^ 3 = 5 |
~ |
取反运算 | 二进制位进行 ! 运算:各二进制码按补码各位取反 | ~ 6 = -7 |
交换 2 个变量的值(方法一:临时变量法)
简单、通用,但需要定义临时变量。
class VariablesExchange{
public static void main(String[] args){
int num1 = 10, num2 = 20;
System.out.println("交换前:num1 = " + num1 + ", num2 = " + num2);
int temp;
temp = num1;
num1 = num2;
num2 = temp;
System.out.println("交换后:num1 = " + num1 + ", num2 = " + num2);
}
}
运行结果:
交换前:num1 = 10, num2 = 20
交换后:num1 = 20, num2 = 10
交换 2 个变量的值(方法二:数值加减法)
不用定义临时变量,但仅限于数值型的变量交换,且可能超出变量的表数范围。
class VariablesExchange{
public static void main(String[] args){
int num1 = 10, num2 = 20;
System.out.println("交换前:num1 = " + num1 + ", num2 = " + num2);
num1 += num2;
num2 = num1 - num2;
num1 -= num2;
System.out.println("交换后:num1 = " + num1 + ", num2 = " + num2);
}
}
运行结果:
交换前:num1 = 10, num2 = 20
交换后:num1 = 20, num2 = 10
交换 2 个变量的值(方法三:位异或交换法)
原理:一个数对另一个数进行两次位异或运算,该数不变 不用定义临时变量,但仅限于数值型的变量交换,但不会超出变量的表数范围。
class VariablesExchange{
public static void main(String[] args){
int num1 = 10, num2 = 20;
System.out.println("交换前:num1 = " + num1 + ", num2 = " + num2);
num1 = num1 ^ num2;
num2 = num1 ^ num2; // 获得原来的 num1,赋值给 num2
num1 = num1 ^ num2; // 获得原来的 num2,赋值给 num1
System.out.println("交换后:num1 = " + num1 + ", num2 = " + num2);
}
}
运行结果:
交换前:num1 = 10, num2 = 20
交换后:num1 = 20, num2 = 10
三元运算符
[条件表达式]为 true,执行[表达式1]
,为 false,执行[表达式2]
,其中,[表达式1]
和[表达式2]
的结果可以用同种数据类型接收:([条件表达式])?[表达式1]:[表达式2];
三元运算符与
if-else
的异同:
- 三元运算符都可简化
if-else
语句;- 三元运算符必须要返回一个结果;
if
后的代码块可有多个语句。
获取三个数中的较大数:
class GetMax{
public static void main(String[] args){
int n1 = 12, n2 = 30, n3 = -43;
int maxTemp = (n1 > n2)? n1 : n2;
int maxResult = (maxTemp > n3)? maxTemp : n3;
System.out.println(maxResult);
}
}
运行结果:
30
运算符优先级
优先级 | 运算符 | 结合性 |
---|---|---|
01 | () 、[] 、{} |
→ |
02 | ! 、+ 、- 、~ 、++ 、-- |
← |
03 | * 、/ 、% |
→ |
04 | + 、- |
→ |
05 | << 、>> 、>>> |
→ |
06 | < 、<= 、> 、>= 、instanceof |
→ |
07 | == 、!= |
→ |
08 | & |
→ |
09 | ^ |
→ |
10 | | |
→ |
11 | && |
→ |
12 | || |
→ |
13 | ?: |
← |
14 | = 、+= 、-= 、*= 、/= 、&= 、|= 、^= 、~= 、<= 、>= 、>>>= |
← |