Java自动装箱与自动拆箱

1、Java基本数据类型及其对应的包装器类类型

Java中共用8种基本数据类型,并为这8种基本数据类型中的每一种都提供了一个包装器类,例如int类型对应的包装器类是Integer。具体类型如下表:
在这里插入图片描述

2、自动装箱和自动拆箱

  • 自动装箱:就是指自动将基本数据类型转换为包装器类型
  • 自动拆箱:就是指自动将包装器类型转换为基本数据类型
Integer i = 19;  // 自动装箱
int j = i;  // 自动拆箱

如上第一行代码,数组19是基本数据类型(int),当赋值给包装器类型(Integer)变量时,触发自动装箱操作,创建一个Integer类型对象,并且赋值给了 i 。其底层实际执行了以下代码:

Integer i = Integer.valueOf(19);

同理,第二行代码,将 i 赋值给 j,触发了自动拆箱操作,将 i 中的数据取出赋值给 j 。其底层实际执行了以下代码:

int j = i.intValue();

JDK5前是手动装箱和拆箱,JDK5之后就可以自动装箱和自动拆箱了。

Object o1 = true ? new Integer(1) : new Double(2.0);   // 1.0

object o2;
if(true) {
	o2 = new Integer(1);  // 1
} else {
	o2 = new Double(2.0);
}

如上第一个三元运算符输出的结果是1.0,因为三元运算符是一个整体,所以前面的Integer受后面的Double影响。而下面的if-else是无影响的,则输出1。

Integer i = 100;  // 自动装箱
String str1 = i +  "";

以上只是以i的基本数值转成了字符串,并不会影响i的数据类型。

3、双等于“=="

通过” == “ 来比较对比的是栈中的值,基本数据类型比较的值,引用数据类型比较的是堆中内存对象的地址。即判断两个对象是否相等,实际上是在判断两个局部变量存储的地址是否相同,即是否指向相同的对象。
但如下代码中比较的a1和a2却是相等的。b1和b2又是不相等的。

Integer a1 = 58;
Integer a2 = 58;
Integer b1 = 129;
Integer b2 = 129;
Integer a3 = new Integer(58);
System.out.println(a1 == a2);  // true
System.out.println(b1 == b2);  // false
System.out.println(a1 == a3);  // false

实际是,Integer运用了 享元设计模式 来复用对象,当通过自动装箱,即调用 valueOf() 来创建Integer对象的时候,如果要创建的Integer对象的值在 -128~127之间,就会从IntegerCache类中直接返回,否则才会调用new方法创建新的对象。即a1和a2都是指向同一个对象(享元对象),b1和b2不是同一个对象。 而Integer a3 = new Integer(58);并不会调用valueOf(),即不会使用到IntegerCache。

为什么IntegerCache只缓存 -128~127之间的整型值?

当IntegerCache类被加载的时候,缓存的享元对象会被集中一次性创建好,而整型数值太多,不能一次性被创建,否则会占用太多的内存,类加载的时间会过长,即只能缓存大部分应用来说最常用的整型值,即一个字节的大小,byte数据类型的范围。
实际,jdk提供了自定义缓存的最大值,设置方法有

方法一: -Djava.lang.Integer.IntegerCache.high = 255
方法二: -XX:AutoBoxChaheMax = 255

4、字符串类型String

 String s1 = "小姐姐";
 String s2 = "小姐姐";
 String s3 = new String("小姐姐");
 System.out.println(s1 == s2);  // true
 System.out.println(s1 == s3);  // false

 System.out.println(s1 == s3.intern());  // true
 System.out.println(s3 == s3.intern());  // false

String类型利用了享元设计模式来复用相同的字符串常量,JVM会专门开辟一块存储区来存储字符串常量,这块存储区叫做”字符串常量池“

Integer类中要共享的对象是在类加载的时候就会集中一次性创建好,而字符串是在第一次被使用时被存储到常量池中,当之后再使用的时候,就直接引用常量池中已经存在的即可。

当调用intern()方法时,如果池已经包含一个等于此String对象的字符串,则返回池中的字符串。否则,将此 String 对象 添加到池中,并返回此String 对象的引用,即 intern()方法 最终返回的是常量池的地址(对象)。s3是重新new了一个对象,所以s3是指向堆的,而s3.intern()是指向常量池的,所以 s3不会等于s3.intern() 。

String s1 = "hello";
s1 = "haha";

String是一个final类,代表不可变的字符序列。字符串是不可变的,一个字符串对象一旦被分配,其内容是不可变的。即以上是先创建hello对象,之后在常量池中找是否有haha对象,没有就创建haha对象,再将s1指向haha对象,共创建了两个对象

String a = "hello" + "abc";

以上编译器会自动优化等价为:(即总共创建了一个对象)

String a = "helloabc";
String a = "hello";  // 创建a对象
String b = "abc";  // 创建b对象

// 先创建一个StringBuilder sb = new StringBuilder()
// 执行 sb.append("hello"); 再执行 sb.append("abc"); 最后转成string :String c = sb.toString()
String c = a + b;  // 最终c指向堆中的对象

Strin d = "helloabc";
System.out.print(c == d);  // false  d指向常量池,c指向堆
  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值