Java数据类型

218 阅读5分钟

基本数据类型

  • 整数型:byteshortint(默认)、long
  • 浮点型:floatdouble(默认)
  • 字符型:char
  • 布尔型:boolean

每个数据类型所占的字节数

数据类型 关键字 内存占用
字节型 byte 1个字节
短整型 short 2个字节
整型 int 4个字节
长整型 long 8个字节
单精度浮点型 float 4个字节
双精度浮点型 double 8个字节
字符型 char 2个字节
布类型 boolean *

boolean类型占的字节数

《Java虚拟机规范》一书中的描述:“虽然定义了boolean这种数据类型,但是只对它提供了非常有限的支持。在Java虚拟机中没有任何供boolean值专用的字节码指令,Java语言表达式所操作的boolean值,在编译之后都使用Java虚拟机中的int数据类型来代替而boolean数组将会被编码成Java虚拟机的byte数组,每个元素boolean元素占8。

经过查阅资料发现,使用int的原因是,对于当下32位的处理器(CPU)来说,一次处理数据是32位(这里不是指的是32/64位系统,而是指CPU硬件层面),具有高效存取的特点。

从上面这句话得出两个结论

  1. 单独使用是4个字节
  2. 在数组中是1个字节

注意事项

  • 浮点型只是近似值不是精确值,比较时需要使用BigDecimal
  • 数据范围与字节数不一定相关,float是4字节、long是8字节,但是float范围比long更广
  • 浮点型默认是double类型,使用float类型需要在后面加上F;整型默认是int,使用long类型需要在后面加上L

编译器的两点优化

  • 对于byteshortchar三种类型,如果右侧赋值的数值没有超过左侧的范围,编译器会自动隐含地帮我们自动补上;如果超过了左侧的范围,直接报错
  • 在给变量进行赋值的时候,如果右侧的表达式中全都是常量,没有任何变量,那么编译器javac会直接将若干个常量表达式计算得到结果。编译时赋值,不用等到运行时再去耗费cpu计算,这称为编译器的常量优化,但是一旦表达式中有其他变量参与(有符号引用),那么就不能进行这种优化了

包装类

  • 为什么要用包装类?

基本数据类型虽然使用非常方便,但是没有对应的方法操作这些基本数据类型,可以使用包装类来操作这些数据。比如:compare、类型转换等

装箱与拆箱

  • 装箱:基本数据类型->包装类型
  • 拆箱:包装类型->基本数据类型
  • 以Integer为例
//装箱
Integer(int value); //过时
static Integer valueOf();
Integer result = Integer.valueOf(x);

//拆箱
int intValue();
int x = result.intValue();
  • 自动装箱与自动拆箱
//自动装箱
Integer result = 1; //相当于Integer result = new Integer(1);

//自动拆箱
result = reuslt + 2; //result + 2相当于result.intValue() + 2;
  • 尽量不要把有自动装箱拆箱的代码写到循环中去 会降低效率

BigDecimal

  • 作用:浮点型之间的等值判断,基本数据类型用==来比较,包装类型不能用equals来比较。原因:精度丢失
  • 使用BigDecimal来定义浮点型数据的值,再进行浮点数的运算操作

包装类型和基本数据类型的区别

  • 从类型来看:包装类型可以用于泛型,基本数据类型不可以。因为在编译过程中会进行泛型擦除,最后只保留原始类型,而原始类型只能是Object类,包装类型都是Object类型,基本数据类型没有原始类型
  • 从值来看:包装类型可以为null,基本数据类型不可以为null。包装类型可以用于POJO,基本数据类型不可以,因为数据库查询结果可能为null,因为要自动拆箱(将包装类型转为基本类型,比如说把 Integer 对象转换成 int 值int intValue()),就会抛出 NullPointerException 的异常。
  • 从比较环节看:基本数据类型用==比较,包装类型需要使用equals,用==比较的是地址值。如果包装类型和基本数据类型进行==比较会自动拆箱
  • 从效率来看:基本数据类型比包装类型更高效。基本类型在栈中直接存储的具体数值,而包装类型在栈中存储的是堆中的引用。很显然,相比较于基本类型而言,包装类型需要占用更多的内存空间。假如没有基本类型的话,对于数值这类经常使用到的数据来说,每次都要通过new一个包装类型就显得非常笨重
  • 从用途来看:局部变量一般使用基本数据类型;POJO的属性一般使用包装类型

包装类的缓存机制

  1. JVM中一个字节以下的整型数据会在JVM启动的时候加载进内存,除非用new Integer()显式的创建对象,否则都是同一个对象。当Integer需要进行自动装箱时,如果数字在 -128 至 127 之间时,会直接使用缓存中的对象,而不是重新创建一个对象
  2. 以Integer为例,当使用自动装箱的方式去创建一个Integer实例时,当数值在[-128,127]之间,会使用缓存中的实例,IntegerCache静态成员内部类,其中一段静态代码是缓存[-128,127]之间的Integer实例。这个范围其实可以调整,通过调整虚拟机参数-XX:AutoBoxCacheMax=<size>来设置缓存的最大值,因为在IntegerCache里面已经预设好了low为-128,但是high是没有显式写出来的,默认是127。
  3. 其他类型也有缓存机制,Byte,Short,Integer,Long为 -128 到 127,Character范围为 0 到 127。除了 Integer 可以通过参数改变范围外,其它的都不行