日期:2014-05-20  浏览次数:20854 次

一个简单而有趣的类型转换问题
默认的整数值类型是int,默认的小数值类型是double。
int a = 56; //数值56默认为int类型
double d = 3.14; //数值3.14默认为double类型



下面问题来了:
float f = 3.14; //编译通过不了。因为无法将一个double类型的值赋给一个float类型的变量。

short s = 56; //编译可以通过。这是为什么呢?将一个int类型的值赋给一个short类型的变量不损失精度么?为什么可以通过编译呢?
类型转换 基本数据类型

------解决方案--------------------
引用:
Quote: 引用:

Java语言是一种强类型的语言。强类型的语言有以下几个要求:
变量或常量必须有类型
要求声明变量或常量时必须声明类型,而且只能在声明以后才能使用。
赋值时类型必须一致
值的类型必须和变量或常量的类型完全一致。
运算时类型必须一致
参与运算的数据类型必须一致才能运算。

在整数之间进行类型转换时,数值不发生改变, jvm会自己做个隐式转换的
Java常用的数据类型,都是有固定顺序的,低类型向高类型转,可以认为是隐式;高类型要转成低类型,必须要显示的转换。

你在

int a = 56; //数值56默认为int类型 ?? 你这是定义a是int类型
short s = 56; //你这是定义s是short类型 

56是个常量 

从汇编角度讲,常量是在指令上的量,就像指令中的立即数寻址,系统取到指令后不需要通过地址再去取数据,它不占空间,也就没有地址的概念,当然有人认为系统对常量和变量都有空间存储,只是对待不同。
而变量是在内存中有个空间对它进行存储,指令保存它的地址。

我们可以认为,常量在编译后所占用的内存大小已经确定,所以常量1在内存里就是二进制1,而变量需要在运行时进行分配,因此要根据变量的数据类型进行空间开辟,也就是先根据数据类型来n个0,然后把1塞进去

需要注意的一点就是int a = 56;这一句,a是a,56是56,虽然值一样。
只看a和56,它们两个都是存放在栈内存中。
我们知道存放在栈中的数据大小与生存期必须是确定的,所以a根据它的数据类型int占用4字节,看到的二进制就是00000000 00000000 00000000 00000000。
再看56,他是个地道的常量,他所占用的栈内存空间就是由它本身确定也就是111000。
然后将56赋值给a,就是将a指向到56,但不管a也好还是56也好,他们本身的内存空间大小都没有发生改变,所以我们看到a的二进制就是00000000 00000000 00000000 00111000,而56则是111000

综上,我们看到的a和56的内存空间是两块不同的内存空间。

你用short s = 56 也是一样的这个步骤,那么既然56 这个常量没有超出short的范围,那么这么定义编译通过有什么不可以?

你要是要是改成这样:
int a = 56;
short s = a;
你可以自己试试看能编译过不?

如果不行 你再改成
int a = 56
short s = (short) a;
这次看看可以了不?


谢谢你打了这么多字

但我还是有些疑问,你说56是个地道的常量,没有超出short范围,所以可以直接赋给short类型的变量对吧。那我想知道3.14是个地道的常量么?它超出float的范围了么?那它为什么不能直接赋给float类型的变量呢?你要是想说3.14默认是double类型的,高精度向低精度赋值要强制类型转换,那我就会继续问56默认是什么类型的?它为什么可以直接赋值给short类型的变量呢? 
我怎么总感觉整型和浮点型的赋值规则不一样啊。呵呵


晚上不上线的,刚上来, 不好意思哈, 你这个问题其实涉及到jvm。 jvm是基于栈的机器,几乎所有的指令都与操作数栈相关。栈操作包括把常量压入操作数栈、执行通用的栈操作、在操作数栈和局部变量之间往返传输值。常量值隐含包含在操作码内部。
比如我敲下如下代码编译一下:
int a  = 56;
short s = 57;//为了区别于a,这边用57
s = (short)(s+1);//你这里可以试试,不加short会怎样?
s+=1;//这里为什么可以,在反汇编生成的字节码里面就看出来了
double d = 3.14;
float f = 3.15f;//如果不加f 为什么会不过,在字节码里面也可以看到
编译一下, 然后用反汇编器看看java编译器为我们生成的字节码。这样就可以对照源代码和字节码,从而了解很多编译器内部的工作。 反汇编后如图


看见没有, 是bipush,实际上一开始认为常量56也好或者57也好, 都是当做byte 隐式转换上来的 没有超过short的范围,当然可以通过。
那么接下来, 我们如果加一句,看看结果又会变成什么样子?


这里9999为什么用sipush?

说明在赋值常量的时候是从低到高的。你甚至可以认为,在jvm里面,常量池的数据类型跟每个数据类型取值范围的定义是一样的,有兴趣的话, 你可以试试看int a =2147483647,看看生成的字节码是什么?