为什么System.out.println(a++) 输出的结果没有自增

小问题大智慧

今晚一个Java初学者小妹来请教我,问了我几个问题

  1. 为什么 int a=10; System.out.println(a++);这段代码输出的结果是10
  2. long a = 2147483648;为什么报错
  3. double a= 10/3为什么等于3.0,而 double a= 10.0/3 却等于 3.3333

呵,这不是简单的不能再简单问题吗。很快啊,我嗖的一下告诉了她答案

  1. System.out.println(a++) 输出结果10,是因为计算机的执行顺序的原因。在这一条语句中,计算机先执行输出语句,此时a的值为10,当输出语句完毕的时候,它才执行自增操作
  2. a的赋值报错,是因为右边的值超出了int的最大取值,所以报错。
  3. double a= 10/3等于3.0是因为 10/3是以int类型计算的,而后将结果强转成long。同理 double a=10.0/3,一开始 10.0就是double类型,是按照double计算的,所以结果为3.3333;

小妹妹明显对我的回答不满意,又问

  • 为什么不先自增再输出,是因为语法和规定吗?
  • long a=2147483648;,我a明明是long的类型,他int的最大值关我long什么事?

啊这,着实难倒了我这个练习时长两年半的砖家。当时我并没有很好的给出的回答,搪塞的过去,一如两年前的我,对这个一知半解。

仔细想想这个问题其实挺基础的,自己没有深刻的理解,真是白读了4年大学,白白练习了两年半。

为什么System.out.println(a++) 输出的结果没有+1

要解释清楚这几个问题,对JVM执行代码的原理要有一定的了解。

  1. JVM执行的是经过编译后的字节码文件,按照指令一条条的执行的
  2. JVM在执行运算时有局部变量表和操作数栈的概念

第一条,比较好理解,略过。第二条具体是什么意思呢,我得好好讲。
先来看一段字节码指令

*****源代码******************

int a=10;
System.out.println(a++);

****编译后的字节码*************

1 bipush 10
2 istore_0
3 getstatic #2 <java/lang/System.out>
4 iload_0
5 iinc 0 by 1
6 invokevirtual #3 <java/io/PrintStream.println>

指令说明

  1. 表示将10进栈顶
  2. 10存入局部变量0
  3. 获取静态类 <java/lang/System.out>
  4. 复制局部变量0的值存入栈顶
  5. 局部变量0的值+1
  6. 执行静态方法 <java/lang/System.out>,输出栈顶的值

注意第5条指令,此时并没有对栈顶元素进行操作

此时此刻局部变量0的值为 11

操作数栈的值为10

System.out.println()方法输出的是栈顶的值。

所以,System.out.println(a++) 输出的结果是10

long a = 2147483648 为什么报错

java编译时会将一些确定而清晰的值划分为常量池变量,比如常用的的静态变量和局部变量。JVM在初始化类的时候会把确定的数据放入常量池中,到了执行指令时直接用就行。

比如以下这些代码,它们的值都会放入常量池中

public static String NORMAL = "normal";
int a = 10;

Java是一种强类型的语言,一个值在放入常量池中就会确定好它的类型。一般数字类型都会以int形式存放(Java编译时会自动判断该变量的类型)。

long a=2147483648,在编译的时候就会报错,因为2147483648这个值,编译器认为它是int类型,但它又超出了int的取值范围,所以不能通过编译;

为什么double a= 10/3等于3.0,而 double a= 10.0/3 却等于 3.3333

最重要的原因: 1010.0在常量池中是不一样的类型

详解语句double a = 10/3

  1. 代码运行时,会将103放入常量池中,它们是int类型
  2. 该语句先计算 10/3,此时两个值都int类型,它们的结果是3
  3. 将计算结果进行强制类型转换,并赋值给a,此时a的值是3

详解语句double a = 10.0/3

  1. 代码运行时,会将10.03放入常量池中,10.0double类型,而3int类型
  2. 该语句先计算10.0/3,此时是将double类型除以int类型,计算的结果为3.3333
  3. 将计算结果赋值给aa的值为3.3333。计算结果的类型是double,所以它不需要强制类型转换再赋值

总结

看似简单的问题,其实深究下去还是不简单啊,对Java的理解不能只是停留在表层,共勉!