Java的基本数据类型
1.基本数据类型
Java的基本数据类型有四类八种,分别是整型、浮点型、字符型、布尔型四类,byte、short、int、long、float、double、char、boolean八种。
1.1 整型
类型 | 存储需求 | 取值范围 | 默认值 |
---|---|---|---|
byte | 1字节 | -128 ~ 127 | 0 |
short | 2字节 | -32768 ~ 32767 | 0 |
int | 4字节 | - 231 ~ 231-1 (刚刚超过20亿) | 0 |
long | 8字节 | - 263 ~ 263-1 | 0L |
1.2 浮点型
类型 | 存储需求 | 取值范围 | 默认值 |
---|---|---|---|
float | 4字节 | 约 ±3.40282347E+38F(有效位数为6~7位) | 0.0f |
double | 8字节 | 约 ±1.79769313486231570E+308(有效位数为15位) | 0.0d |
通常情况下,float类型的精度并不能满足需求,因而浮点型字面量的默认类型为double。
1.3 字符型
char类型需要使用2字节存储空间,使用字面量赋值时需要使用单引号括起来(双引号表示字符串),默认值为 ‘u0000’。
char类型原本用于表示单个字符,但随着世界发展,有些字符需要使用两个char值才能存储,char类型逐渐不能满足需要,因而除非必要,否则不建议使用char类型。
1.4 布尔型
boolean类型只有 true 和 false 两个值,用于进行逻辑判断,默认值为 false,占用空间并没有确切给出,具体可参考以下文章 。
java中boolean类型基础数据在内存中占用的空间大小分析
2. 数据类型转换
2.1 数据类型转换
上图表示Java中的合法类型转换,其中6个实线箭头表示转换时无精度丢失,虚线箭头表示可能发生精度丢失。
当一个整型操作数和一个浮点型操作数用二元操作符连接时,会先将操作数转为同一类型进行计算。
- 当两个操作数有一个是 double 类型时,另一个操作数会转为 double 类型;
- 否则,如果有一个操作数是 float 类型,另一个操作数会转为 float 类型;
- 否则,如果有一个操作数是 long 类型,另一个操作数会转为 long 类型;
- 否则,两个操作数都会被转为 int 类型。
除此之外,还可以使用强制类型转换进行类型转换,其语法格式是在圆括号中写入希望转换的目标类型,然后紧跟待转换的变量,如
double x = 9.96;
int cx = (int) x;
2.2 long 自动转为 float
思考一个问题,long 类型占用8字节,float 类型占用4字节,为什么 float 类型可表示的范围比 long 类型要大,同时 long 类型转为 float 类型会发生精度丢失?
要回答这个问题,我们需要了解整型和浮点型在内存中的存储结构,long 类型是有符号的二进制补码表示的整数,而 float 采用 IEEE754 标准进行表示,其结构为:
- 第一位是符号位
- 接下来的8位是指数域
- 剩下23位是小数域,取值范围是 [1,2) 或 [0,1)
符号位用 S 表示,指数域用 E 表示,小数域用 M 表示,则计算方法为 (-1)S × M × 2E ,其中指数域 E ,最大为255,因此 float 表示范围比 long 要大。
同时,二进制和小数并不是一一对应的,如 0.3,因而 long 类型转为 float 类型可能会丢失精度。
3. 包装类
3.1 基本类型和包装类出现的原因
Java中一切皆对象。但基本类型比较特殊,它们特别小、简单,若是将其使用 new 存储在堆中,往往不是特别有效,因此,对于这些类型,Java不使用 new 创建变量,而是创建一个并非是引用的“自动”变量,将这个变量的值直接存储在堆栈中,这样会更加高效。
但这使得基本类型并不具备对象的性质,为了和其他对象“接轨”于是出现了包装类。举个例子,在使用集合类型时,其中必须存储对象而不能是基本类型。
3.2 自动装箱和自动拆箱
自动装箱和自动拆箱是Java1.5才有的功能。
简单来说,自动装箱就是自动将基本数据类型转换为包装器类型;自动拆箱就是自动将包装器类型转换为基本数据类型。
通过反编译可以得知,自动装箱是调用对应包装类的 valueOf 方法,而自动拆箱是调用包装类的 xxxValue 方法。
public class Main {
public static void main(String[] args) {
//自动装箱,这里会自动执行 Integer.valueOf(int) 方法
Integer ir1 = 100;
Integer ir2 = 100;
Integer ir3 = 200;
Integer ir4 = 200;
//自定拆箱,这里会自动执行 ir1 .intValue 方法
int i = ir1;
}
}
这里有个点需要注意,通过查看源码我们可以得知,Byte、Short、Integer、Long 这四个类型将 [-128, 127] 区间的整数都放在了cache中,因此上述例子中变量 ir1 和 ir2 实际指向同一个地址,而ir3和 ir4则指向不同的地址。除此之外,还可以看到 Character 类型同样缓存了数据,而 Float 和 Double 没有。
3.3 自动装箱和自动拆箱发生的时机
- 赋值和传参的时候,需要包装类就装箱,需要基本类型就拆箱
- 当包装类与基本类型用算数操作符(±*/%)连接时,会进行拆箱操作
- 当包装类与基本类型用 == 操作符连接时,会进行拆箱操作