基本数据类型
内置类型,是Java中不同于类(Class)的特殊类型
Java基本类型共有八种,基本类型可以分为三类:
字符类型 | char |
---|---|
布尔类型 | boolean |
整数类型 | btype\short\int\long |
浮点数类型 | float\double |
- Java中还存在另外一种基本类型void
对应的包装类 java.lang.Void,不过我们无法直接对它们进行操作
基本数据类型的优点:
Java语言中,new一个对象是存储在堆里的,我们通过栈中的引用来使用这些对象;所以,对象本身来说是比较消耗资源的。
- Java提供了基本数据类型,这种数据的变量不需要使用new创建,他们不会在堆上创建,而是直接在栈内存中存储,因此会更加高效
注意: 基本数据类型超出一定的表示范围,发生溢出时并不会抛异常
包装类
包装类均位于java.lang包
基本数据类型 | 包装类 |
---|---|
byte | Byte |
boolean | Boolean |
short | Short |
char | Character |
int | Integer |
long | Long |
float | Float |
double | Double |
为什么需要包装类:
为了让基本类型也具有对象的特征,就出现了包装类型,使得它具有了对象的性质,并且为其添加了属性和方法,丰富了基本类型的操作
拆箱与装箱
装箱:把基本数据类型转换成包装类的过程就是打包装 ,boxing
拆箱: 把包装类转换成基本数据类型的过程就是拆包装,unboxing
在Java SE5之前,要进行装箱,可以通过以下代码:
Integer i = new Integer(10);
在Java SE5中,Java提供了自动拆箱与自动装箱功能
- 自动装箱: 就是将基本数据类型自动转换成对应的包装类。
- 自动拆箱:就是将包装类自动转换成对应的基本数据类型。
Integer i =10; //自动装箱
int b= i; //自动拆箱
Integer i=10 可以替代 Integer i = new Integer(10);
- 不需要手动去new一个Integer对象
自动装箱与自动拆箱的实现原理:
自动拆装箱的代码:
public static void main(String[]args){
Integer integer=1; //装箱
int i=integer; //拆箱
}
对以上代码进行反编译后可以得到以下代码:
public static void main(String[]args){
Integer integer=Integer.valueOf(1);
int i=integer.intValue();
}
- int的自动装箱都是通过**Integer.valueOf()**方法来实现的
- Integer的自动拆箱都是通过integer.intValue来实现的
自动装箱都是通过包装类的valueOf()方法来实现的
自动拆箱都是通过包装类对象的xxxValue()来实现的。
哪些地方会自动拆装箱:
- 将基本数据类型放入集合类
List<Integer> li = new ArrayList<>();
for (int i = 1; i < 50; i ++){
li.add(i);
}
将上面代码进行反编译,可以得到以下代码:
List<Integer> li = new ArrayList<>();
for (int i = 1; i < 50; i += 2){
li.add(Integer.valueOf(i));
}
当把基本数据类型放入集合类中的时候,会进行自动装箱
- 包装类型和基本类型的大小比较
Integer a=1;
System.out.println(a==1?"等于":"不等于");
Boolean bool=false;
System.out.println(bool?"真":"假");
对以上代码进行反编译,得到以下代码:
Integer a=1;
System.out.println(a.intValue()==1?"等于":"不等于");
Boolean bool=false;
System.out.println(bool.booleanValue?"真":"假");
包装类与基本数据类型进行比较运算,是先将包装类进行拆箱成基本数据类型,然后进行比较的
- 包装类型的运算
Integer i = 10;
Integer j = 20;
System.out.println(i+j);
反编译后代码如下:
Integer i = Integer.valueOf(10);
Integer j = Integer.valueOf(20);
System.out.println(i.intValue() + j.intValue());
两个包装类型之间的运算,会被自动拆箱成基本类型进行
- 三元运算符的使用
boolean flag = true;
Integer i = 0;
int j = 1;
int k = flag ? i : j;
反编译后代码如下:
boolean flag = true;
Integer i = Integer.valueOf(0);
int j = 1;
int k = flag ? i.intValue() : j;
这其实是三元运算符的语法规范:当第二,第三位操作数分别为基本类型和对象时,其中的对象就会拆箱为基本类型进行操作
如果这个时候i的值为null,那么就会发生NPE。(自动拆箱导致空指针异常)
例如:
public static void main(String[] args) {
Map<String, Boolean> map = new HashMap<>();
Boolean b = map != null ? map.get("test") : false;
System.out.println(b);
}
==》Exception in thread "main" java.lang.NullPointerException
反编译一下该代码:
public static void main(String args[]){
Map map = new HashMap();
Boolean b = Boolean.valueOf(map == null ? false : ((Boolean)map.get("test")).booleanValue());
System.out.println(b);
}
hashmap.get(“test”)->null;
(Boolean)null->null;
null.booleanValue()->报错
解决:
Map<String,Boolean> map = new HashMap<String, Boolean>();
Boolean b = (map!=null ? map.get("test") : Boolean.FALSE);
保证了三元运算符的 第二第三位操作数都为对象类型 。这样就不会发生自动拆箱操作,以上代码得到的b的结果为null
- 函数参数与返回值
//自动拆箱
public int getNum1(Integer num) {
return num;
}
//自动装箱
public Integer getNum2(int num) {
return num;
}
自动拆装箱与缓存:
Java SE的自动拆装箱还提供了一个和缓存有关的功能
public static void main(String... strings) {
Integer integer1 = 3;
Integer integer2 = 3;
if (integer1 == integer2){
System.out.println("integer1 == integer2");
}else{
System.out.println("integer1 != integer2");
}
Integer integer3 = 300;
Integer integer4 = 300;
if (integer3 == integer4){
System.out.println("integer3 == integer4");
}else{
System.out.println("integer3 != integer4");
}
}
结果:
integer1 == integer2
integer3 != integer4
在Java中,==比较的是对象应用,而equals比较的是值
所以,在这个例子中,不同的对象有不同的引用,所以在进行比较的时候都将返回false。
但,这里两个类似的if条件判断返回不同的布尔值
原因就和Integer中的缓存机制有关
在Java 5中,在Integer的操作上引入了一个新功能来节省内存和提高性能:
整型对象通过使用相同的对象引用实现了缓存和重用
适用于整数值区间-128 至 +127。
只适用于自动装箱。使用构造函数创建对象不适用。
当需要进行自动装箱时,如果数字在-128至127之间时,会直接使用缓存中的对象,而不是重新创建一个对象
其中的javadoc详细的说明了缓存支持-128到127之间的自动装箱过程。最大值127可以通过-XX:AutoBoxCacheMax=size修改。
在Java 5中引入的时候,范围是固定的-128 至 +127。后来在Java 6中,可以通过java.lang.Integer.IntegerCache.high设置最大值
在Boxing Conversion部分的Java语言规范(JLS)规定如下:
- 如果一个变量p的值是:
-128至127之间的整数(§3.10.1)
true 和 false的布尔值 (§3.10.3)
‘\u0000’至 ‘\u007f’之间的字符(§3.10.4)
范围内的时,将p包装成a和b两个对象时,可以直接使用a==b判断a和b的值是否相等
自动拆装箱带来的问题:
- 包装对象的数值比较,不能简单的使用==,虽然-128到127之间的数字可以,但是这个范围之外还是需要使用equals比较
- 有些场景会进行自动拆装箱,由于自动拆箱,如果包装类对象为null,那么自动拆箱时就有可能抛出NPE
- 如果一个for循环中有大量拆装箱操作,会浪费很多资源