浅谈Java中的自动包装机制

曾经终败给现在 2022-01-30 01:04 299阅读 0赞

浅谈Java中的自动包装机制

  • 不可变对象
    • 引入概念
    • 解释
  • 自动包装机制
    • 拆包和自动装箱
    • 常量池
    • 包装类的创建方式
    • 代码
  • String类
    • String类的自动包装区别
    • String的缺陷
    • StringBuffer和StringBuilder

不可变对象

引入概念

在Java中,除了基本类型以外,其他都是以对象的形式存在。而基本类型也有其对应的类,这些类被称为包装器(Wrapper)。例如






















基本类型 对应的类
int Integer
double Double
以下 同理

这些类就属于不可变对象

解释

通常我们创建一个类时,会设置相应的get和set方法来对成员进行修改。但是对于不可变对象而言,内部成员的值是不能被修改到的,所以被称为不可变

对于用户自定义的类而言,若想设计成不可变对象,可以借助private或者final关键字防止使用者来修改对象内部的值。

对于Integer这些对象包装器来说,他们不能够被继承,也不能修改内部的值。(因为他们其实就是final类)例如,调用构造函数: new Integer(1) 来实例化一个内部保存了一个int=1的Integer对象,这个对象内部的值再也不能被修改了。这样做的好处是,将Java里的所有基本类型都统一为了对象,而且可以将一些工具类放入包装器中。所以当我们想定义一个整数数组列表时,因为<>尖括号中是不允许是基本类型的,这时候就可以使用Integer对象包装器类。但是在数组列表中,调用list.add()方法时,可以直接传入一个整数类型int,这是因为Java中存在有自动包装机制。

自动包装机制

拆包和自动装箱

借着上面的那个例子,例如调用如下代码

  1. ArrayList<Integer> list = new ArrayList<Integer>();
  2. list.add(2);

实际上第二句话相当于

  1. list.add(new Integer(2));

这里就发生了一个自动装箱过程(Autoboxing)

由于Integer是不可变对象,那么如果我们调用如下语句的时候,会发生什么呢?

  1. Integer n = 3;
  2. Integer m = 2;
  3. n++;
  4. m+n;

第一句话,等号右边是个int基本类型,所以这里先进行自动装箱,将3变成一个Integer类。
但是在第二句话中,由于Integer不能参与运算,这里会发生拆箱动作,将Integer类变成基本类型int参与运算,执行自增动作,在运算结束之后,再自动装箱
拆箱动作会发生在Integer类型被当作int使用时(例如:打印操作)。
接下来在第三句中,两个Integer类型要发生运算关系,所以两个Integer都会先拆箱变为基本类型相加,然后结果再打包。

关于自动装箱还有几点需要说明

  1. 由于包装器类的引用可以为null,所以在拆箱的时候会抛出NullPointerException
  2. 在一个条件表达式中混合使用Integer和Double类型,Integer值会拆箱,提升为double,再装箱为Double

常量池

Java在栈里设计了一个静态常量池,里面已经准备好了一些会经常用到的数字(0~127)或者字符串(比如”abc”),所以当实例化对象的值在常量池里有的时候,Java不会去堆中再开辟新的空间,而是直接指向栈内的那个值。

包装类的创建方式

常量化赋值

Integer a = 10;

放在栈内存储,将被常量化,相当于在栈内开辟一个对象给a,但是由于a是10,是被常量化的 直接指向栈里那个10,以后是凡10的也都指同一个地址。但是如果是128那些就要自己重新在栈里开辟了,因为他们是常量池之外的。

new对象创建

Integer c = new Integer(10);

放在堆内,不会被常量化, 就算是10,也不能指到栈里那个10去。因为是再堆中重新开辟的。

代码

下面是一段测试代码,涉及到了常量池和自动装箱机制:

  1. public class BoxClassTest {
  2. public static void main(String[] args) {
  3. int i1 = 10;
  4. Integer i2 = 10; //自动装箱
  5. System.out.println(i1 == i2); //true
  6. //i2会被自动拆箱比大小
  7. //Ps.
  8. //在两边都是对象的时候 == 是比较地址
  9. //两边都是基本类型的时候 == 是比较值
  10. //当有有一边是对象,有一边是基本类型时,会自动拆箱比较。
  11. Integer i3 = new Integer(10); //在堆中开辟一块空间
  12. System.out.println(i1 == i3); //true
  13. //虽然i3在堆里 但是直接拆箱比大小
  14. System.out.println(i2 == i3); //false
  15. //两个都是Integer,一个的地址在栈里一个的地址在堆里
  16. Integer i4 = new Integer(5);
  17. Integer i5 = new Integer(5);
  18. System.out.println(i1 == (i4+i5)); //true
  19. System.out.println(i2 == (i4+i5)); //true
  20. System.out.println(i3 == (i4+i5)); //true
  21. //i4+i5的操作会暴力拆箱 括号那一团就变成基本类型了
  22. //第三个:对象比基本类型,自动给对象拆箱
  23. Integer i6 = i4 + i5; //i4+i5会自动拆箱 也就是 i6 = 10,然后再自动装箱
  24. System.out.println(i1 == i6); //true //拆箱操作
  25. System.out.println(i2 == i6); //true //一样的
  26. System.out.println(i3 == i6); //false //一个在栈里一个在堆里
  27. }
  28. }

String类

String类的自动包装区别

语言描述有点难,直接看代码和注解吧:

  1. public class StringConstantTest {
  2. public static void main(String[] args) {
  3. String s1 = "abc";
  4. String s2 = "abc";
  5. String s3 = "ab"+"c";
  6. String s4 = "a"+"b"+"c";
  7. System.out.println(s1==s2); //只要后面都是常数 就直接被编译器当成abc
  8. System.out.println(s1==s3); //所以这里三个全是true
  9. System.out.println(s1==s4); //true
  10. //对于String来说,如果你是给的一堆常量,Java会自动把他们包装起来
  11. //所以上面的全都是abc
  12. String s5 = "c"
  13. String s6 = "ab" + s6;
  14. System.out.println(s1==s6); //false
  15. //但是比如你相加的东西里面有个变量,那么编译器就无法识别出来了
  16. //他就不能帮你直接组合好了
  17. //所以他会在代入s3的值之后在堆内重新开辟一块地址
  18. //所以这里的abc就不是和常量池里的是一个地址了
  19. }
  20. }

String的缺陷

以这段代码为例:

  1. public class StringTest {
  2. public static void main(String[] args) {
  3. String str = "a";
  4. str = "ab";
  5. }
  6. }

由于String也是一个不可变对象,所以每次改变String引用变量的时候,我们并没有去修改实际字符串的内容,而是重新指向了新的一个对象。
在这里插入图片描述
显然,单是为了增加一个b字母,我们又重新创建了一个新的对象,而且原先的a还被闲置了(虽然会被回收),这样的效率太低了,所以,Java里提供了另外两个操作字符串的可变对象类。

StringBuffer和StringBuilder

StringBuffer:线程安全,但是速度没用StringBuilder快。
StringBuilder:线程没那么安全,但是快些。

使用方法如下:

  1. StringBuffer str = "a";
  2. for(int i = 0;i<3;i++)
  3. str.append("b");
  4. System.out.println(str);

这时结果输出的str就是abbb了,而且没用创建新的对象。同理StringBuilder。

发表评论

表情:
评论列表 (有 0 条评论,299人围观)

还没有评论,来说两句吧...

相关阅读

    相关 Java异常处理机制

    Java中的异常处理机制是Java语言的一个核心特性,它允许程序在遇到错误时能够优雅地处理,而不是直接崩溃。以下是Java异常处理机制的一些基本概念和要点: ###1.异常类

    相关 Java异常处理机制

    在Java编程中,异常处理机制是用于处理程序运行时可能出现的错误情况的一种方法。以下是对Java中异常处理机制的简要理解: 1. **抛出异常(Throwing Except

    相关 java反射机制

    反射,当时经常听他们说,自己也看过一些资料,也可能在设计模式中使用过,但是感觉对它没有一个较深入的了解,这次重新学习了一下,感觉还行吧! 一,先看一下反射的概念:

    相关 java反射机制

    一、java的反射机制浅谈   最近研究java研究得很给力,主要以看博文为学习方式。以下是我对java的反射机制所产生的一些感悟,希望各位童鞋看到失误之处不吝指出。受到各

    相关 Java自动包装机制

    刚刚开学,今天课少,趁着闲再更新一篇关于Java中泛型博文,废话不多说直接上干货。 任何基本类型都不能作为类型参数。这应该是一个基本的概念,先看看下面的代码: i

    相关 JAVA反射机制

    什么是反射? 在java核心卷一给出的概括: 能够分析类能力的程序称为反射,可以在运行时分析类能力,运行时查看对象。 按照我的理解就是: 1. 探索类的信息 2.