本文基于JDK 1.8.0_45
基本数据类型
Java中的基本数据类型及其所占字节大小如下所示。基本数据类型是在栈中字节分配内存,其中一个char为两个字节,这样一个char中即可存储一个中文汉字。
类型
字节
byte
1
char
2
short
2
integer
4
long
8
float
4
double
8
boolean
1/8
注:1 byte是8 bits
在定义long的时候需要在数字末尾加上l或者L,在定义float的时候可以在数字末尾加上f或者F(默认如果数字中有小数点则为float),在定义double的时候需要在数字末尾加上d或者D。一般建议在数字末尾加上大写字母来标示数据类型,这是因为特别是小写字母l和数字1在印刷体中较难区分,使用大写字母L更易于阅读。如:
byte b = 1;
char c = 1;
short s = 1;
int i = 1;
long l = 1L;
float f = 1F;
double d = 1D;
在各种数据类型之间可以进行转换,转换顺序如下所示:
byte -> short, char -> int -> long
float -> double
int -> float
long -> double
包装类
在Java中基本数据类型是存储在栈中,不是对象。为了在一些需要使用对象的场景中使用这些基本数据类型,Java给每种数据类型定义一种相对应的包装类,对应关系如下。
类型
包装类型
byte
Byte
char
Character
short
Short
integer
Integer
long
Long
float
Float
double
Double
boolean
Boolean
这些基本类型和包装类型可以直接进行转换,这被称为自动装箱、拆箱。自动装箱、拆箱是一个比较消耗内存的操作,尽量避免使用,下面会介绍Java对这个操作的一些优化。
Number
其中Byte、Short、Integer、Long、Float、Double均继承自abstract类Number,在Number类中定义了获取这六种基本数据类型数据的方法,其中方法byteValue和方法shortValue是通过先获取intValue再将integer的数据强转为byte或short类型实现的。
基本方法
在每种Number类型中均定义了最大值、最小值、基本数据类型的Class对象、二进制长度和字节长度,另外还提供了众多的util方法,如有符号无符号转换,与各种进制(Java目前支持2-36进制,其他均需要自行实现)的string的互相转换,从系统属性中直接获取包装类型的属性值的get*。
parse*和valueOf方法的区别是前者返回的是基本数据类型,后者返回的是包装类的实例。
Integer中有一个很赞的方法decode,接收一个string,根据string中的标示自动判断该数据的进制并转换为Integer。
二进制相关方法方法
另外还有一些二进制相关的方法,以下以Integer中的方法为例:
System.out.println(“Integer.toBinaryString(5) = ” + Integer.toBinaryString(5));// 5的二进制为101
System.out.println(“Integer.highestOneBit(5) = ” + Integer.highestOneBit(5));// 最高位置为1,其他为0:二进制为100, 十进制为4
System.out.println(“Integer.lowestOneBit(5) = ” + Integer.lowestOneBit(5));// 最低位置为1,其他为0:二进制为001, 十进制为1
System.out.println(“Integer.numberOfLeadingZeros(5) = ” + Integer.numberOfLeadingZeros(5));// 二进制中开头有多少个0:29
System.out.println(“Integer.numberOfTrailingZeros(5) = ” + Integer.numberOfTrailingZeros(5));// 二进制中结果有多少个0:0
System.out.println(“Integer.bitCount(5) = ” + Integer.bitCount(5));// 二进制中有多少个1:2
System.out.println(“Integer.rotateLeft(5, 2) = ” + Integer.rotateLeft(5, 2));// 向左旋转:二进制为10100,十进制为20
System.out.println(“Integer.rotateRight(5, 2) = ” + Integer.rotateRight(5, 2));// 向右旋转:二进制为1000000000000000000000000000001,十进制为1073741825
System.out.println(“Integer.reverse(16777216) = ” + Integer.reverse(16777216));// 将二进制反转:128
System.out.println(“Integer.signum(-100) = ” + Integer.signum(-100));// 符号位为负时返回-1
System.out.println(“Integer.signum(0) = ” + Integer.signum(0));// 入参为0时返回0
System.out.println(“Integer.signum(100) = ” + Integer.signum(100));// 符号位为正时返回1
System.out.println(“Integer.reverseBytes(1) = ” + Integer.reverseBytes(1));// 按字节反转:16777216
System.out.println(“Integer.reverseBytes(16777216) = ” + Integer.reverseBytes(16777216));// 按字节反转:16777216
除以0?
一般我们认为0是不能作为分子进行除法运算的,否则会抛出ArithmeticException异常。但是事实上在Java中对浮点数做除法运算的时候0可以可以作为除数的,如下
System.out.println(-1.0F / 0.0F);// -Infinity
System.out.println(1.0F / 0.0F);// Infinity
System.out.println(0.0F / 0.0F);// NaN
System.out.println(1 / 0);// java.lang.ArithmeticException: / by zero
Number的缓存——享元模式
所谓享元模式即为将一部分最经常使用的对象缓存在内存中,但需要使用这些对象的时候直接从内存中获取已经实例化好的对象而不是重新实例化一个,这样可以很好的节约内存并减少对象实例化的开销。
上面说到自动装箱、拆箱是一个比较消耗内存的操作,而数字是程序中最经常使用的东西,为了解决这个问题Java使用享元模式对这个操作进行了优化,对Byte、Short、Integer、Long的缓存范围是-128~127。其中Integer的缓存最大值是可以在JVM启动的时候通过-XX:AutoBoxCacheMax=size进行自定义。如以下程序在执行的时候添加参数-XX:AutoBoxCacheMax=130即可全部返回true。
System.out.println(Integer.valueOf(“127”) == Integer.valueOf(“127”));
System.out.println(Integer.valueOf(“128”) == Integer.valueOf(“128”));
Boolean
Boolean中定义了true和false的实例、基本数据类型的class对象,与基本数据类型boolean、string直接的互相转换,另外还提供了一些and、or、xor的util方法。
Character
Character对自动装箱拆箱也做了跟Number相关类型类似的优化,对0-127之间的char进行了缓存。另外提供了很多其他的util方法,比如大小写转换、与string、number之间的互相转换等。