Java数据类型最全讲解
这篇文章我们学习java的基本数据类型,这也是面试喜欢考的基本内容,所以还是仔细过一遍吧。
首先我们要知道java有两大数据类型:
原始数据类型(又名内置数据类型)
引用数据类型
基本数据类型比较简单,比如int、long。 引用数据类型类似于 C++ 指针,它指向一个对象。 我们使用的所有对象或数组都属于引用数据类型,它的默认值为null。
本文重点介绍基本数据类型,但也会讲解这些基本类型对应的包装类,也就是引用类型。
1.八种基本数据类型
Java有8种基本数据类型,需要记住
4 种整数类型:byte、short、int、long
2 种浮点类型:float、double
1 字符类型:char
1 布尔类型:布尔
看到这些熟悉的名字,你应该完全明白什么是基本数据类型了。 简单的说,它们其实就是常用的小写数据类型。
下图显示了它们占用的位数和字节数。 Boolean没有明确的定义,一般只占一位。
基本型
位数
字节
最小值
最大值
默认值
字节
8个
1个
-128
127
短的
16
2个
-32768
32767
整数
32
4个
-2147483648
2147483647
长的
64
8个
-9223372036854775808
9223372036854775807
0L(L不区分大小写,但是小写容易和1混淆,所以一般都是大写)
字符
16
2个
'u0000'
'uFFFF'
'u0000'(即一个空格)
漂浮
32
4个
1.4 E-45
3.4028235 E38
0f
双倍的
64
8个
4.9 E-324
1.7976931348623157 E308
0d
布尔值
1个
没有最小值和最大值
没有最小值和最大值
错误的
E是科学记数法。 E后面的数字表示E前面的数字乘以10。例如3.14E-3乘以10的负三次方:
3.14 x 0.001 =0.00314
那么这些最大值和最小值可以通过它们的包装类中的两个常量MAX_VALUE和MIN_VALUE来查看,例如:
System.out.println("Integer最小值:" + Integer.MIN_VALUE);
System.out.println("Integer最大值:" + Integer.MAX_VALUE)
关于什么是wrapper类,第三节会详细解释java科学计数法转换成数字,这里先剧透一下,就是Integer、Double等基本数据类型对应的大写类。
2.char数据的赋值
char 类型比较特殊。 它是用java编码的Unicode,占用2个16位字节,无符号,所以它的范围是0-65535。 可以理解为可以存储65536个不同的符号,但是计算机不能直接存储符号。 所以它实际上是用整数存储的,只是用Unicode编码来表示字符。 每个字符都有一个专属的固定Unicode编码,所以每个数字对应一个符号。
另一种赋值方式是通过16位Unicode编码,比较特殊,可能用得少,格式为'u1234'。 'u'表示这是一个Unicode字符,里面的四位其实就是16进制的Unicode编码。 char的包装类中的MAX_VALUE和MIN_VALUE这两个常量也是这样存储的字符。
例如:将'u7801'或整数30721赋值给char,结果等于character:code。 如果以10进制计算7801,你会发现结果是30721。
所以可以通过三种形式给它赋值
① 用单引号括起来的单个字符
② 0-65535之间的整数(Unicode值的十进制)
③用单引号括起来的字符编码(Unicode值的十六进制)
当然结果和范围一样,最后只能表示一个字符,所以我们一般使用第一种方式赋值。 只是当你看到有人赋一个整数,结果是一个未来的字符时,不要大惊小怪。
例如:
public static void main(String[] args) {
char a = 'A';
char b = 65;
char c = 'u0041';
System.out.println(a);
System.out.println(b);
System.out.println(c);
}
输出结果是一样的,都是:A
所以以后不要下意识的认为char字符'6'转换成整数也是6,最后其实要看Unicode值,结果应该是整数:54
3. 包装
这8种基本类型都有对应的包装类,也就是java中的另一种数据类型:引用数据类型,它有自己的属性和方法,默认值为null。
我们平时在使用基础数据的时候,也更多的是用到它们的包装类。 因为这些基本数据类型只能声明和存储一个值,甚至不能为null,也没有我们常用的类型转换或equals()方法。 基本数据类型只能用于计算,而封装类比较完备,适合业务使用。
基本数据类型对应的封装类有:Byte、Short、Integer、Long、Float、Double、Character、Boolean。 也就是说,除了char对应Character,int对应Integer外,其他的都只是大写。
注意:这里要注意String。 它既不是基本类型也不是包装类。 不要把它误认为是字符类型的基本类型,虽然它的重要性不低于基本类型。
字符类的基本类型只有字符。
4. 自动装箱和拆箱
你应该听说过自动拆箱。 它是针对基本类型和它们的封装类,让它们自动转换。
装箱:用相应的应用程序类型包装原始类型
拆箱:将包装器类型转换为原始数据类型
这个说起来可能不是很清楚,但是这个功能我们一直都在用。 比如在java se5之前,如果要初始化一个Integer对象,需要这样:
Integer i = new Integer(10);
看起来很正常很别扭,因为每一个new对象都是通过new来初始化的,但是好像我们在使用的时候从来没有遇到过这样的麻烦。 一般来说,我们是:
Integer i=10;
这是自动装箱。 理论上10是数据的基本类型,只能直接赋值给int类型。 现在我们把它赋值给int的包装类Integer。 其实java自动将基础数据10装箱,打包到包装类中。 简单地说,为你创建一个 Integer 对象,并将值存储为 10。
自动拆箱则相反。 Java自动将Integer变量拆箱为具体的整型值,这样包装类变量就可以直接赋值给int基本数据类型。
自动拆箱:
Integer i=10; //自动装箱
int j=i; //自动拆箱
大多数包装类和基本类型混合的地方,都会自动进行拆箱。 下面第 6 节将详细介绍自动拆箱的场景。
5、自动拆箱的实现原理
通过编译和反编译类字节码文件,我们就可以知道答案了
先写一个这样的测试代码:
public class Test {
public static void main(String[] args) {
Integer i=10;
int j=i;
}
}
打开cmd输入javac Test.java编译得到Test.class
然后用jd-gui工具反编译Test.class得到如下代码:
public class Test
{
public static void main(String[] paramArrayOfString)
{
Integer localInteger = Integer.valueOf(10);
int i = localInteger.intValue();
}
}
可以看出,自动拆箱的核心是在包装类变量上使用两种方法。 打包的时候就是Integer.valueOf()。 通过查看源码我们可以知道本质上是调用了new Integer初始化。 拆箱的时候是intValue(),也是直接返回int的值。
貌似高端的所谓自动拆箱,其实只是偷偷调用了包装类的两个方法,只是我们没有发现罢了。 另外,其他基本类型的开箱同理。 比如char的两个方法分别是valueOf()和charValue()。 你可以自己测试一下。
总结:装箱过程是通过调用wrapper的valueOf方法实现的,拆箱过程是通过调用wrapper的xxxValue方法实现的。
6.自动拆包场景
除了我们最常见的assignment,还有一些场景会进行自动拆箱。 下面可以反编译查看原理
① 将基本数据类型放入集合类
例如
List li = new ArrayList();
for (int i = 1; i < 50; i ++){
li.add(i);
}
这里使用自动装箱:li.add(Integer.valueOf(i));
②比较大小
Integer a=1;
System.out.println(a==1);
结果是真的,其实这里用到了自动拆箱:System.out.println(a.intValue()==1);
③ 手术
Integer i = 1;
System.out.println(i+2);
这里使用了自动拆箱,只有拆成基本类型才能进行操作:System.out.println(i.intValue()+2);
④ 三元运算符
这种情况比较少见,我也是在网上搜集资料才知道的。
boolean flag = true;
Integer i = 0;
int j = 1;
int k = flag ? i : j;
这里使用自动拆箱:int k = flag ? i.intValue() : j,当第二个和第三个操作数分别是原始类型和对象时,对象将自动拆箱。 所以一不小心,可能会触发空指针异常,所以要注意这一点。
这些场景中最需要注意的是计算部分。 以后遇到封装类的计算时,要记住它们是拆箱成基本数据类型再计算的。
7.不同基本类型之间的运算①不同基本类型之间
在java中,除了boolean,其他基本类型都可以自动转换。 转换的形式是从低到高java科学计数法转换成数字,即当两个不同的基本类型一起操作时,低类型将按以下顺序转换为高类型。
byte, short, char——“int”——“long”——“float——”double
byte、short 和 char 是特殊的。 即使不涉及高等类型,在比较运算时也会直接自动转为int进行处理。 比如short、int、float类型相加,在运算前都会全部转换为三种float类型。
②基本类型和String
如果基本类型用String操作,会自动转换为String类型,但这里有以下特殊情况:
System.out.println(1+2+"3"+4);
这里从左到右,由于1+2是两个整数,所以会先计算3,再把3+"3"转成String。 同样,“33”+4 将变成字符串 334。
8.包装类和常量池
Java的基本类型的包装类大多实现了常量池技术:Byte、Short、Integer、Long、Character、Boolean,即传递一定范围的缓存,避免重复创建对象。
①Byte、Short、Integer、Long默认创建[-128,127]范围内的数据
②字符默认创建[0,127]范围内的数据
③Boolean默认返回True或false
这些可以通过源码的valueOf()方法查看,也就是你在任何地方通过valueOf()创建基础数据(自动装箱)的时候,都是判断同一个常量池中是否有这个值。 如果有就直接返回常量池中的对象,如果没有就给你一个新的对象。
因此,如果通过autoboxing或者valueOf获取到一个wrapper对象,当数据的值在缓存范围内时,无论获取多少次相同的数据,本质上都指向同一个对象,所以内存地址和喜欢自然也是平等的。
至于浮点数,是没有缓存的,因为一定范围内的整数是有限的,而浮点数是无限的。 即使范围是0到1,浮点数也是数不胜数,很难定位哪些浮点数是常用的,所以没有浮点数的常量池缓存。
参考:
一、什么是Java自动拆箱:
2、深入剖析Java装箱拆箱:
3、java不同基本类型之间的操作: