Java笔记——lang-包装类

1 包装类

1.1 概念

  • 包装类都在java.lang中,无需额外导入
  • 包装类就是把基本数据类型包装成引用类型
  • 相较于基本数据类型,包装类型是一个类/属性/方法,通过创建对象来使用,可以实现接口和继承其他类
  • 基本数据类型存储的是值本身,直接保存在栈内存中,而包装类是指向存储该类型值的对象的引用,因此保存在堆内存中

1.2 数据类型

表格第一行为数据基本类型,第二行为其对应的包装类型

byte short int long float double char boolean void
Byte Short Integer Long Float Double Character Boolean Void
  • 注意包装类型的默认值为null(因为是创建类的对象),而基本数据类型通常为其对应数据格式的0值
  • void类是最终类,没有子类,也没有返回值。构造方法为私有,所以不能创建对象(同时防止被外界错误的创建实例)

Byte Short Integer Long Float Double 等数据类型,都有一个共同的 Number 父类

同时通过查看Java源码得知,每个数据基本类型对应的包装类的缓存范围取决于其valueOf()方法中定义的cache数组长度

  • 对于Byte来说,在-128到127之间返回同一个对象。不会超出范围

  • 对于Short、Integer、Long来说,在-128到127之间,返回的是同一个对象,超出这个范围,返回新的对象

对于Float、Double来说,每次都是创建新的对象

对于Character来说,0-127返回相同对象,否则返回新的对象

对于Boolean来说,要么返回TRUE、要么返回FALSE

但值得注意的是,与我们平时使用的true和false不同,在源码中,Boolean类返回的TRUE和FALSE是对象,对象指向的值为ture和false(只有在Java源码中这样,我们在实际使用中可不能把他当成对象)

1.3 自动装箱

自动装箱是jdk5引入的特性,底层调用的是valueOf(类型)

Integer i = 10;

在编译时实际上调用了 Integer.valueOf(int) 方法来创建一个 Integer 对象,等价于执行:

Integer i = Integer.valueOf(10);
  • 优点:
    1.避免了类型进行显示转换,提高代码的简约和美观性
    2.Java API的设计趋势统一为对象,这有助于简化API的设计,也有利于开发者的体验统一
    3.集合框架(如Map,List,Set,Collection等)只能存储对象,例如可以在List中保存任何对象,但不可以存在List
    4.相较基本数据类型更加灵活,对象拥有状态和行为(调用方法),而不仅仅是一个值,同时对象可以设置为null值,符合更多使用场景,比如List集合找不到想要的元素时可以返回null值
    5.对象是多态性的体现,而多态是Java的核心特征之一。基本数据类型由于没有类的层次结构,无法体现多态性。

  • 缺点:
    1.自动装箱带来了极大便利,但同时带来了额外的性能开销,尤其是在处理大量数据的情况下
    2.自动装箱的结果是对象,因此可能会产生null值,在编写程序的过程中需要注意处理空值的情况,否则程序可能会抛出NullPointerException异常
    3.只有特定范围内(通常为-128到127)的整数在自动装箱时才会被缓存,超出这个范围的值每次都会产生一个新的对象

  •     public langDemo01() {
            Integer integer1 = 80;
            Integer integer2 = 80;
            Integer integer3 = 180;
            Integer integer4 = 180;
            Integer integer5 = integer4;
            System.out.println(integer1 == integer2);
            System.out.println(integer3 == integer4);
            System.out.println(integer4 == integer5);
        }

    运行截图:

    在Java中,== 运算符比较的是地址而非值,integer1和integer2的值都在-128到127的缓存范围内,因此他们引用的都是一个对象实例,所以integer1 == integer2的结果为true

    而integer3和integer4对应的值都在缓存的范围之外,即使值相同,也重新创建了两个对象,所以地址是不同的,结果为false

    integer5指向与integer4相同的对象的引用,地址是相同的,结果为true

    1.4 自动拆箱

    自动拆箱同样是JDK5引入的新特性,他是编译器把包装类对应的对象转换为基本数据类型值的过程,在Java中,每种基本数据类型都有对应的包装类

    下面是一些拆箱示例:

    1.赋值给基本数据类型

    Integer integers = new Integer(10);//已过时的方法
    //等价于Int int1 = Integer.valueOf(10);
    int int1 = integers;//自动拆箱
    //等价于 int int1 = integers.intValue();//手动拆箱

    2.通过方法传参

    public static void main(String[] args){
        printInt(10);
    }
    public static void printInt(int i) {
        System.out.println(i);
    }

    3.数组初始化

    Integer[] integerArray = {1, 2, 3}; // 自动拆箱
    for (Integer i : integerArray) {
        System.out.println(i);
    }

    这里,我们在初始化integerArray时,编译器会自动将每个整数字面量转换成Integer对象。然后当我们遍历数组时,可以使用这些对象。

    4.方法返回值

    public static Integer getInteger() {
        return 300;
    }
    public static void main(String[] args) {
        int result = getInteger();
        System.out.println(result);
    }

    当涉及到null值时(因为Integer可以是null),自动拆箱可能会导致NullPointerException,所以在处理可能为null的包装类对象时需要注意处理null值情况


    将字符串转换为指定数据类型的用法:

      int anInt = Integer.parseInt("134");
      double v = Double.parseDouble("3.14");
      long l = Long.parseLong("76");
      boolean aTrue = Boolean.parseBoolean("TRue"); //通过下图源码得知这里的true不区分大小写

    • 使用parseBoolean方法转换时,不必区分大小写,因为其Java源码使用了equalsIgnoreCase方法进行比对
    System.out.println(aTrue);
    System.out.println(l);

    字符串如何转换成char类型?

    首先声明一个字符串:

    String str = "absdf123";

    我们可以先使用String类的toCharArray方法将其转换为字符数组,然后再通过for循环输出字符数组的内容

    char[] chars = str.toCharArray();
    for (int i = 0; i < chars.length; i++) {
        System.out.print(chars[i]);
    }

    也可以直接是用String类的charAt方法根据索引获取字符

    for (int i = 0; i < str.length(); i++) {
        System.out.print(str.charAt(i));
    }

    • Java 中的一些基本类型的包装类(如 Integer, Double, Character, Boolean 等),已经重写了 hashCode() 方法来提供合适的哈希码值,让我们看看源码中是如何定义的:

    在Interger类中,hashCode()方法直接返回其封装的int值


    Double 类的 hashCode()方法将Double值的bit无符号右移32位


    由于double 类型在内存中是以64位(8字节)的二进制形式存储,所以首先调用doubleToRawLongBits方法将 double 类型的浮点数直接从内存中的二进制位模式读取出来。将这个二进制位模式原封不动地转换成一个 long 类型的整数,之后将他返回而不考虑任何如NaN等特殊情况

    doubleToRawLongBits是Java的一个native方法,因此它的实现依赖于JVM和底层的操作系统。我们无法直接在Java源码中查看该方法的具体实现细节。


    ]

    与doubleToRawLongBits相似,也是将double类型的浮点数转换为其IEEE754位模式表示,如果value是NaN(not a number),它会对NaN做标准化处理,返回0x7ff8000000000000L。


    ]

    Character 类的hashCode()方法返回字符的Unicode值


    Boolean类的hashCode()方法对于true返回1231,对于false返回1237


    在Java中哈希码是32位的,而long类型的数据是64位,Long类的hashCode()方法中通过无符号右移32位,截取32位的高位值作为哈希码返回


    对于Short和Byte类,需要将原数据提升到32位(4字节)的int类型并返回


    Float类型和Double类型的机制相同,这里不做赘述

    浏览量: views