Java共有8中基本数据类型:byte、boolean、char、short、int、float、long、double。
对应的包装类分别为:Byte、Boolean、Character、Short、Integer,Float,Long,Double
1. byte、Byte
byte表示一个字节,它的取值范围时-128~127。
public static final byte MIN_VALUE = -128;
public static final byte MAX_VALUE = 127;
思考:为什么byte取值范围时-128~127呢?
首先,复习以下原码、反码、补码的概念。
- 原码: 最高位为符号位(0为正,1为负)表示的二进制表示形式。
- 反码: 正数的反码为其本身,负数的反码符号位保持不变,其余位取反。
- 补码: 正数的补码为其本身,负数的补码为其反码+1。
- 补码转原码: 正数的补码就是原码,负数的补码-1后,符号位不变,其余位取反后得原码
为什么要有原码、反码和补码呢?因为计算机电路中只有加法器,而没有减法器,所以只能通过转换为补码的形式来计算。计算机中的有符号数都是以补码表示的。
举例:-3+1 = -2
十进制 | 原码 | 反码 | 补码 |
-3 | 10000011 | 11111100 | 11111101 |
1 | 00000011 | 00000011 | 00000011 |
补码相加:11111101+00000011=11111110
再转换为原码:保持符号位不变,补码j=11111110,取反得1000
过程 | 结果 |
补码 | 11111110 |
补码-1 | 11111101 |
取反 | 10000010 |
十进制 | -2 |
再回过头看byte的取值范围。
- 正数: 00000000 ~ 01111111(补码)。
- 负数: 10000000 ~ 11111111(补码)
以上是计算机里的补码表示形式,把他们转为原码:
补码 | 原码 | 十进制 |
00000000 | 00000000 | 0 |
01111111 | 01111111 | 127 |
10000000 | 11111111 | -128 |
11111111 | 10000001 | -1 |
换算成十进制后范围便为-128~127。
总结:byte的范围是-128 ~ 127是因为,8位有符号整数的表示范围为-127 ~ 128。
2. boolean、Boolean
boolean即布尔值,取值为true或false。
💡 注意: boolean和Boolean在使用lombok生成getter/setter时有所不同。请看如下例子:
public class BooleanGetterSetter { @Data public static class Man { private String name; private boolean isMan; private boolean boy; private Boolean isGirl; } public static void main(String[] args) { Man man = new Man(); //对于基本类型boolean定义的变量,生成setter时,如果是is开头的,则默认省略is man.setMan(true); man.setBoy(true); //对于包装类型Boolean定义的变量,生成setter时,则按照直接加set前缀且首字母变为大写的方式生成 man.setIsGirl(false); //同样的,对于生成的getter也是如此 boolean giril = man.getIsGirl(); boolean isMan = man.isMan(); boolean isBoy = man.isBoy(); } }
3. char、Character
char代表一个Unicdoe字符,2个字节,16位。
💡 注意: char只能表示范围在2个字节(即一个代码单元)内的字符,超过2个字节的无法表示,如这个笑脸表情😀,char c = '😀'
则会编译不通过,因为它超过了char的范围,此时只能用String来存储,我们可以通过new String(“😀”)的方式,在idea中断点看到它真实的值:实际上它是由两个Unicdoe编码组成的:
\uD83D
和
\uDE00
。我们可以通过String来还原这个笑脸:
char[] smail = new char[]{'\uD83D','\uDE00'}; System.out.println(String.valueOf(smail)); //输出😀
Character是char的包装类,一般开发中使用场景并不多,最常用的还是String,偶尔会使用到它的大小写判断和转换:
Character.isLowerCase();
Character.isUpperCase();
Character.toLowerCase();
Character.toUpperCase();
4. short、Short
short是短整型,16位,2个字节,和char长度相同,不过short是有符号整数,所以它的范围是-32768~32767,同样的,它的范围计算方式和byte的计算方式一致。short的使用场景也不是很多,在确认数据不会超出short范围后,可以使用short来节省空间。
5. int、Integer
int是最常用的整型,32位,4字节,取值范围位-2147483648 ~ 2147483647,即-231~-231-1。
@Native public static final int MIN_VALUE = 0x80000000;
@Native public static final int MAX_VALUE = 0x7fffffff;
💡注意:Integer默认缓存了-128~127的值,并且最大值是可以通过参数-Djava.lang.Integer.IntegerCache.high=<sizie>
配置的
// high value may be configured by property
int h = 127;
String integerCacheHighPropValue = sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
因此如果Integer的值在-128~127之间时,如下输出都是true:
public class IntAndInteger {
public static void main(String[] args) {
//缓存范围内,可以使用==比较,超出范围的需要用equals
Integer a = 127;
Integer b = 127;
Integer c = 128;
Integer d = 128;
System.out.println(a == b);
//输出:true
System.out.println(c == d);
//输出:false
System.out.println(c.equals(d));
//输出:true
}
}
6. float、Float
float表示单精度浮点型,32位,4字节。其中,1为符号位,8位指数位(无符号),23位尾数位,十进制取值范围为:1.4E-45 ~ 3.4028235E38。其二进制的表示形式以及计算方式,可参考《计算机组成原理》一书。
在Java开发中,我们需要注意,float的精度有限,有些数字不能精确表示,因此如果需要精确计算(如金额)的时候,float并不合适。
Float是float的包装类,需要注意的是Float虽然提供了compare和equals方法,但是需要注意要比较的数是否在精度范围内,否则会发生数值不相等compare却为0或equals为true的情况。另外,应该静止使用浮点数来作为循环变量。
💡 提示:对于高精度要求的数值计算,需要使用BigDecimal
7. long、Long
long表示长整型,64位,8字节。
Long是long的包装类,和Integer类型一样,它也缓存了-128~127。
//最小值 -2^63
@Native public static final long MIN_VALUE = 0x8000000000000000L;
//最大值 2^63-1
@Native public static final long MAX_VALUE = 0x7fffffffffffffffL;
8. double、Double
double表示双精度浮点型,64位,8字节。其中,1为符号位,11位指数位(无符号),52位尾数位,十进制取值范围为:4.9E-324 ~ 1.7976931348623157E308,除范围和精度外和float基本相同。
9. 特殊类型void
void是一个特殊的类型,有人把它归到Java基本数据类型中,是因为可以通过Class.getPrimitiveClass("void")
获取对应的原生类型,所以如此看来Java的基本数据类型就有9种,实际上,这仅仅是一个分类问题,不同的分类规则,结果也不同。
值得注意的是,void有个对应的类型Void,可以把它看做是void的包装类,Void的作用主要作用有以下2个:
- 泛型占位
当我们定义了泛型时,如果没有写泛型参数,idea等开发工具会给出提醒,建议写上泛型类型,但实际上却是不需要固定的泛型类型,这时候据可以写上Void来消除警告,例如ResponseData<Void>
- 反射获取void方法
Method[] methods = String.class.getMethods();
for (Method method : methods) {
if(method.getGenericReturnType().equals(Void.TYPE)){
System.out.println(method.getName());
}
}
//输出:
//getBytes
//getChars
//wait
//wait
//wait
//notify
//notifyAll