1、操作符、char、byte、short计算类型提升、前置++、后置++

98 阅读3分钟

1、操作符注意事项

  • 操作运算符号
  • 几乎所有的操作服只能运用于基本类型,==、=!、= 除外,包装类型若果运用操作服进行计算时,会进行自动拆箱和装箱操作。

	Integer i = 10;  
	Integer j = 11;  
	System.out.println(i+j);
  • jvm指令解析
//jvm指令解析
 0 iconst_3 //将变量3推送至栈顶
 1 invokestatic #1 <java/lang/Integer.valueOf : (I)Ljava/lang/Integer;> //调用Integer.valueOf方法,把栈顶元素转换成包装类型,并压入栈顶
 4 astore_1 //将栈顶元素赋值给变量1
 5 iconst_4
 6 invokestatic #1 <java/lang/Integer.valueOf : (I)Ljava/lang/Integer;>
 9 astore_2
10 getstatic #2 <java/lang/System.out : Ljava/io/PrintStream;>
13 aload_1 //将本地变量1压入栈顶
14 invokevirtual #3 <java/lang/Integer.intValue : ()I> //转换为int类型
17 aload_2 //将本地变量2压入栈顶
18 invokevirtual #3 <java/lang/Integer.intValue : ()I> //转换为int类型
21 iadd //栈顶两个元素相加
22 invokevirtual #4 <java/io/PrintStream.println : (I)V>
25 return

1、运算操作符只能运用于基本数据类型,当包装类型进行计算时会先转化为基本数据类型 2、比较运算符进行比较时,任何类型都可以

2、类型转换(类型提升、窄化)

  • 包装类型在进行的时候会进行类型的转化
  • char、byte、short在进行计算时会进行自动提升为int类型
byte b = 1;  
short s = 10;  
short s2 = (short) (b + s);
  • jvm指令解析
0 iconst_1 //常量1入栈
 1 istore_1 //赋值给变量1
 2 bipush 10 //常量10入栈
 4 istore_2 //赋值给变量2
 5 iload_1 //变量1押入操作数栈
 6 iload_2 //变量2押入操作数栈
 7 iadd //计算相加
 8 i2s //转化成short类型
 9 istore_3 //赋值给变量3
10 return

由于Java 指令集的操作码总数不能超过256个,所以不能为每种类型都规定相应的指令,因此只能先转化成int类型进行计算,然后再转化成相应的制定类型。 更深层的原因char、byte、short的大小。 由于Java 栈上分配的最小单位 slot 就是 4 字节,所以char、byte、short在栈中都是占用的四个字节。但是他们会在堆中产生区别解释

  • 关于精度损失问题[[2、浮点类型精度损失]]问题

3、前置++、后置++、+=

  • 代码
int i = 10;  
i = i++;  
i = ++i;  
i+=i;  
i+=10;  
System.out.println(i);
  • 字节码分析
 0 bipush 10  //常量10入栈
 //i++
 2 istore_1 //栈顶元素出栈赋值给变量1
 3 iload_1 //变量1入栈
 4 iinc 1 by 1 //变量1自增1,此时的自增1是在局部变量表中完成的。此时操作数栈中的数依然是10
 7 istore_1 //栈顶元素弹出赋值给变量1
 //++i
 8 iinc 1 by 1 //变量一自增1.在局部变量中完成的,此时变量在局部变量中存储的是11
11 iload_1 //变量1入栈
12 istore_1 //栈顶元素出栈并赋值给变量1
//i+=i
13 iload_1 //变量1入栈
14 iload_1 //变量1入栈
15 iadd //栈顶的2个元素出栈相加,结果入栈
16 istore_1 //栈顶元素出栈赋值给变量1
//i+=10
17 iinc 1 by 10 //局部变量自增10,在局部变量表中进行
20 getstatic #2 <java/lang/System.out : Ljava/io/PrintStream;>
23 iload_1
24 invokevirtual #4 <java/io/PrintStream.println : (I)V>
27 return

通过字节码进行分析发现: 前置++:他是先进行自增操作,也就是在局部变量表中进行完自增操作之后,然后在压入操作数栈,之后的计算都是自增后的值。 后置++:他是后进行自增操作。他先把数值压入操作数栈,然后在变量表中进行自增操作。 例如:sout(i++);sout(i):第一个打印的是操作数栈中的值,第二句打印的时候,局部变量表中的新值已经入栈,打印的是新的值。 注意+=:当加等常量的时候,也是在局部变量表中进行的