小问题大智慧
今晚一个Java初学者来请教我,问了我几个问题
- 为什么 int a=10; System.out.println(a++);这段代码输出的结果是10
- long a = 2147483648;为什么报错
double a= 10/3
为什么等于3.0
,而double a= 10.0/3
却等于3.3333
呵,这不是简单的不能再简单问题吗。很快啊,我嗖的一下告诉了她答案
System.out.println(a++)
输出结果10,是因为计算机的执行顺序的原因。在这一条语句中,计算机先执行输出语句,此时a的值为10,当输出语句完毕的时候,它才执行自增操作- a的赋值报错,是因为右边的值超出了int的最大取值,所以报错。
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执行代码的原理要有一定的了解。
- JVM执行的是经过编译后的字节码文件,按照指令一条条的执行的
- 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>
指令说明
- 表示将
10
进栈顶- 将
10
存入局部变量0
中- 获取静态类
<java/lang/System.out>
- 复制
局部变量0
的值存入栈顶- 将
局部变量0
的值+1
- 执行静态方法
<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
最重要的原因: 10
和10.0
在常量池中是不一样的类型
详解语句double a = 10/3
- 代码运行时,会将
10
、3
放入常量池中,它们是int
类型 - 该语句先计算
10/3
,此时两个值都int
类型,它们的结果是3
- 将计算结果进行强制类型转换,并赋值给
a
,此时a
的值是3
详解语句double a = 10.0/3
- 代码运行时,会将
10.0
、3
放入常量池中,10.0
是double
类型,而3
是int
类型 - 该语句先计算
10.0/3
,此时是将double
类型除以int
类型,计算的结果为3.3333
- 将计算结果赋值给
a
,a
的值为3.3333
。计算结果的类型是double
,所以它不需要强制类型转换再赋值
总结
看似简单的问题,其实深究下去还是不简单啊,对Java的理解不能只是停留在表层,共勉!