java篇 常用工具类 0x05:基本类型的自动装箱拆箱

2024-09-01 02:20

本文主要是介绍java篇 常用工具类 0x05:基本类型的自动装箱拆箱,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

文章目录

    • 数字基本类型的封装类和常用方法
    • 字符基本类型的封装类和常用方法
    • 布尔基本类型的封装类和常用方法

java 从第一个版本开始,就为每种基本类型提供了封装的类,以便可以将其当作类而非基本数据类型使用。

比如 List、Map 这些类,都是操作 Object,无法操作基本数据类型。你无法用 int 作为 Map 的 key 或 value,所以 java 允许让 int 封装成其封装类 Integer 后来作为 Map 的 key 或 value。

java 提供了自动为每种基本数据类型和其对应的封装类之间的自动转换功能。

  • 自动封箱(auto boxing):从基本类型到封装类;
  • 自动拆箱(auto unboxing):从封装类到基本类型。

java 是通过创建实例(如 Integer.valueOf(int类型数据) 来对 int 进行封箱,或直接 new Integer(int类型数据))或返回缓存住的实例来实现自动封箱的,通过调用相应的转换方法实现自动拆箱的(如 int 是通过 Integer 调用 .intValue() 来拆箱获得)。

  • 所谓的“返回缓存实例”
    java 创建一个实例是需要一系列复杂操作的:调用构造方法、分配一块内存等。
    而拆箱和封箱又是频繁发生的事情,所以 java 会缓存住一些实例,比如说 Integer,java 会缓存住256个Integer实例(-128~127,通过 Integer 的内部类 IntegerCache 类来实现,在类加载时创建了 256 个Integer实例),进行自动封箱时,java 会先去找找有没有这个缓存的实例,没有的话再去创建。

// java 会对 -128~127 进行缓存,即类加载时,缓存 256 个 Integer 对象  Integer i1 = 1;  
Integer j1 = 1;  
System.out.println(i1==j1); // true  
// == 判断的是是否为同一个对象,因此 i1 和 j1 是同一个对象  
// 可见直接将数字 1 封箱,其实并没有创建新的对象,而是直接用了缓存的 Integer 对象  Integer j2 = new Integer(1);  
System.out.println(i1==j2); // false  
// 因此,用 new Integer(1) 的方式是真的创建了一个新的 Integer 对象,因此和缓存中的 Integer 对象不是同一个对象  // 检验缓存上界  
Integer i127 = 127;  
Integer i128 = 128;  
Integer j127 = 127;  
Integer j128 = 128;  
System.out.println(i127==j127); // true  
System.out.println(i128==j128); // false  
// 说明的确上限只换存到了 127  
// 检验缓存下界  
Integer i_128 = -128;  
Integer i_129 = -129;  
Integer j_128 = -128;  
Integer j_129 = -129;  
System.out.println(i_128==j_128); // true  
System.out.println(i_129==j_129); // false  
// 说明的确下限只缓存到了 -128

[!warning] 封装类之间是无法相互转换的
基本类型 int 是可以自动转换成 double 的,甚至 double 可以强制转换成 int(只是会造成精度丢失:直接截取整数部分,而非对小数四舍五入成整数)。
但 Integer 与 Double 互相无法自动转换或强制类型转换。

另外封装类型也是不可变的。如 Integer 是没有 .set()方法的。这些和 String 是一样的,对象一旦创建出来就是不能变的(当然,你可以让同一个引用指向另一个对象,但是对象本身是不可改变的)。所以可以放心地将封装类型作为 Map 的 key。

数字基本类型的封装类和常用方法

和数字相关的基本数据类型为:byte、short、int、long、float、double;

和数字相关的基本数据类型对应的封装类依次为:Byte、Short、Integer、Long、Float、Double。

// Byte
public final class Byte extends Number implements Comparable<Byte> {...}// Short
public final class Short extends Number implements Comparable<Short> {...}// Integer
public final class Integer extends Number implements Comparable<Integer> {...}// Long
public final class Long extends Number implements Comparable<Long> {...}// Float
public final class Float extends Number implements Comparable<Float> {...}// Double
public final class Double extends Number implements Comparable<Double>{...}

可以看到,数字基本类型的封装类,都是 Number 类的子类。

下面以 int 与 Integer 为例,介绍数字基本类型的自动拆箱、自动封箱,以及常用方法。

import java.util.HashMap;  
import java.util.Map;  public class WrapperClassesForNumber {  public static void main(String[] args) {  int a = 100;  // new Integer() 可以接收 int 或 String 来创建一个 Integer 实例  Integer i1 = new Integer(a);  Integer i2 = new Integer("123");  // Integer i3 = "345"; // 报错  // Integer.valueOf() 接收一个 int,返回一个 Integer,但是这个 Integer 能直接赋值给 int 引用b,这是因为这个赋值过程 java 会自动拆箱。  int b = Integer.valueOf(a);  // Integer.parseInt() 则是接收一个 String,返回的本身就是一个 int,然后赋值给一个 int 引用c,所以这里没有自动拆箱。  int c = Integer.parseInt("321");  System.out.println("----------自动封箱和拆箱----------");  // java 提供了自动为每种基本数据类型和其封装类之间的转换功能  // 拆箱就是把封装类的值取出来,赋给其对应的基本类型。  // 封箱则是把基本类型封装成其对应的封装类的一个实例。  // 自动,则是指 java 会在有这种转换需求的时候,自动帮你完成这种转换(封箱或拆箱)  Integer ab = 200; // 自动封箱  int cd = ab; // 自动拆箱  // 自动封箱为 Integer,作为 Map 中的 key。  Map<Integer, String> int2Str = new HashMap<>();  // 你不能写成 Map<int,String> int2Str,声明类型是需要用封装类的,但用的时候,java 会帮你自动封箱,把传入的 int 封装成 Integer// .put() 按定义是要传入 Integer 与 String 的  int2Str.put(1, "一");  int2Str.put(2, "二");  int2Str.put(3, "三");  // .get() 按定义是要传入 Object 的,这里同样是将 int 自动封装成了 Integer,然后赋给 Object。  System.out.println(int2Str.get(1)); // 一  // 自动拆箱为 int,并给 key 赋值  for (int key : int2Str.keySet()) {  // .keySet() 迭代出来的元素是 Integer,你让它赋值给 int 引用(key),java 就会对其自动拆箱成 int。  System.out.println(key);  }  /*  1  2  3  */  // Integer 封装类的常用方法  System.out.println("----------常用方法----------");  // Integer.toBinarySring() 接收 int,返回这个整数的二进制,并将这个二进制数用 String 方式返回  System.out.println(Integer.toBinaryString(1024)); // 10000000000  System.out.println(Integer.toBinaryString(-1024)); // 11111111111111111111110000000000  // Integer.toOctalString() 接收 int,返回这个整数的八进制,并将这个八进制数用 String 方式返回  System.out.println(Integer.toOctalString(1024)); // 2000  System.out.println(Integer.toOctalString(-1024)); // 37777776000  // Integer.toHexString() 接收 int,返回这个整数的十六进制,并将这个十六进制数用 String 方式返回  System.out.println(Integer.toHexString(1024)); // 400  System.out.println(Integer.toHexString(-1024)); // fffffc00  System.out.println("----------Number类----------");  // 所有和数字相关的封装类,都继承自 Number 抽象类  Number num = 9; // 这里其实就是用父类 Number 引用指向子类 Integer 实例,但实际传入的是 int,因此这里 java 也是做了自动封箱,把 int 封装成 Integer。  num = new Integer(12345);  Number numD = 9.99; // 同样,这里是父类 Number 引用指向子类 Double 实例,而传入的是 double,所以 java 也是做了自动封箱,把 double 封装成 Double。  numD = new Double(12.345);  // 使用 Nubmer 类可以方便地使用数字类型的转换  // 当然所有的类都继承了 Nubmer 的这些转换方法  System.out.println("使用Number将Double转为byte:" + numD.byteValue()); // 使用Number将Double转为byte:12  System.out.println("使用Number将Double转为short:" + numD.shortValue()); // 使用Number将Double转为short:12  System.out.println("使用Number将Double转为int:" + numD.intValue()); // 使用Number将Double转为int:12  System.out.println("使用Number将Double转为long:" + numD.longValue()); // 使用Number将Double转为long:12  System.out.println("使用Number将Double转为float:" + numD.floatValue()); // 使用Number将Double转为float:12.345  System.out.println("使用Number将Double转为double:" + numD.doubleValue()); // 使用Number将Double转为double:12.345  /*  其实所谓的自动拆箱,就是 java 自动调用了该基本数据类型的封装类的 .xxxValue() 方法,返回了对应的基本数据类型。  比如 Integer 自动拆箱成 int,就是调用了 Integer 的实例方法 .intValue(),返回了 int 类型的值。  */  // 自动拆箱可能会引起NPE异常(NullPointerException)  int2Str.put(null, "无");  System.out.println(int2Str.get(null)); // 无  // 上面这两句并没有涉及自动拆箱或封箱,因为没有发生基本类型和引用类型的转换。  // 自动拆箱为 int,并给 key 赋值,但是有一个 key 为 null,null 是无法转成一个 int 的  // (注意:null 不是 0,0是实实在在的一个数,0 的 int 是可以和 Integer 自动转换的,但 null 其实是“空”,是引用类型的缺省值,但并不能转成 int)。  // 其实自动拆箱,后面是 java 帮我们调用了对应的方法,在这里就是 intValue() 这个方法  // 所以当引用为 null 的时候自动拆箱,相当于是调用 null 的方法,所以这时候会发生 NPE// 对于其他封装类型的自动拆箱,也是一样的。  // for (int key : int2Str.keySet()) {  // // 这里因为 .keySet() 迭代出来的元素中除了有 Integer 还有一个 null,  // // Integer 可以自动拆箱成 int,但 null 转成 int 时,同样自动调用 .intValue() 时,因为无法对 null 调用实例方法,就会报错 NPE。  //     System.out.println(key);  // }  // /*  // 运行报错:NullPointerException  // */  /*  其实这里引出了一种用法:就是,数字基本类型是没有 null 的说法的,  声明一个数字基本类型的成员变量,默认就赋予一个缺省值 0,你无法区分这个 0 是初始值还是你后面赋的值,你无法让它是空的(来表明你从来没有对它赋值)。  这时候,你就可以考虑不使用数字的基本类型,而使用其对应的封装类。其缺省值就是 null,可以用来表明你从没有对它赋值。  所以在有数据交换的时候,建议使用封装类。这样就允许你把它设置成 null,来表明你从没有对它赋过值。  */  }  }

字符基本类型的封装类和常用方法

字符基本类型 char 对应的封装类是 Character。

Character 中有很多叫 isXXX() 的静态方法,比较实用。

而至于字符基本类型和其封装类的自动封箱和拆箱,就不赘述了,和数字类型的拆箱封箱是一样的,都是 java 在我们需要的时候会自动完成转换。

public class WrapperClassesForChar {  public static void main(String[] args) {  // char 对应的封装类是 Character,里面有很多 isXXX 方法比较实用,  // 比如判断字符是否为数字 isDigit() 可以接收 char 或 int,返回 boolean。  System.out.println(Character.isDigit('A')); // false  System.out.println(Character.isDigit('啊')); // false  System.out.println(Character.isDigit('9')); // true  System.out.println(Character.isDigit('0')); // true  // System.out.println(Character.isDigit('123')); // 报错,因为 char 是单字符的  // 其实isDigit()一般常用的是 char 作为形参,也比较直观判断一个字符是否是数字  // isDigit() 接收的是 int 的话,含义有点绕,是判断这个 int 对应的 unicode 对应的编码是否用来表示数字(因为可能是用来表示一个字母、符号而非数字)  System.out.println(Character.isDigit(0x06f8)); // true (因为'u06F0' ~ 'u06F9', 扩展阿拉伯语,印度语数字)  System.out.println(Integer.toString(0x06f8)); // 1784  System.out.println(Character.isDigit(1784)); // true  System.out.println(Character.isDigit(9)); // false  System.out.println(Character.isDigit(37)); // false  System.out.println(0x37); // 55  System.out.println(Character.isDigit(55)); // true  System.out.println(Character.isDigit(0x37)); // true (因为'u0030' ~ 'u0039', ISO-LATIN-1 数字('0' through '9'))  /*  这是因为  'u0030' ~ 'u0039', ISO-LATIN-1 数字('0' through '9')  'u0660' ~'u0669', 阿拉伯 - 印度文数字  'u06F0' ~ 'u06F9', 扩展阿拉伯语,印度语数字  'u0966' ~ 'u096F', Devanagari 数字  'uFF10' ~ 'uFF19', 全角数字  当然也不只有这些范围是数字,还有别的范围也有数字  */  // isXXX 还有诸如判断字符大小写的 isLowerCase() 与 isUpperCase() ,  // 也都是可以接收 char 或 int,但常用的都是接收 char,  // 接收 int 的含义判断也和 isDigit 类似,比较绕,是判断对应unicode表示的字符是否是大写/小写字母,这里就不深究了  System.out.println(Character.isLowerCase('A')); // false  System.out.println(Character.isLowerCase('a')); // true  System.out.println(Character.isUpperCase('A')); // true  System.out.println(Character.isUpperCase('a')); // false // 非字母的字符自然没有所谓的大小写,所以通通都是 false。System.out.println(Character.isLowerCase('3')); // falseSystem.out.println(Character.isUpperCase('3')); // falseSystem.out.println(Character.isUpperCase('0')); // false  System.out.println(Character.isUpperCase('一')); // false}  
}

布尔基本类型的封装类和常用方法

布尔基本类型 boolean 的封装类是 Boolean。

由于 boolean 比较特殊,只有两个值 true 和 false,所以 Boolean 特意定义了两个静态常量:
Boolean.TRUEBoolean.FALSE(注意都是字母大写的,不仅仅是首字母)。

public class WrapperClassesForBoolean {  public static void main(String[] args) {  System.out.println("----------静态变量----------");  System.out.println(Boolean.TRUE); // true  System.out.println(Boolean.FALSE); // false  // 其实这里也是自动拆箱了,因为这两个静态变量的类型是 Boolean,而非其对应的基本类型 boolean。  System.out.println(Boolean.TRUE == true); // true  System.out.println(Boolean.FALSE == false); // true// 上面两句同样是自动拆箱了  System.out.println("----------valueOf----------");  // 只有不区分大小写的 true 才是 true,剩下的字符串都是 falseSystem.out.println(Boolean.valueOf("true")); // true  System.out.println(Boolean.valueOf("TruE")); // true  System.out.println(Boolean.valueOf("tRue")); // true  System.out.println(Boolean.valueOf(" true")); // false (因为多了一个空格)  System.out.println(Boolean.valueOf("false")); // false  System.out.println(Boolean.valueOf("abcd")); // false  }  
}

这篇关于java篇 常用工具类 0x05:基本类型的自动装箱拆箱的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1125669

相关文章

Java实现检查多个时间段是否有重合

《Java实现检查多个时间段是否有重合》这篇文章主要为大家详细介绍了如何使用Java实现检查多个时间段是否有重合,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录流程概述步骤详解China编程步骤1:定义时间段类步骤2:添加时间段步骤3:检查时间段是否有重合步骤4:输出结果示例代码结语作

Java中String字符串使用避坑指南

《Java中String字符串使用避坑指南》Java中的String字符串是我们日常编程中用得最多的类之一,看似简单的String使用,却隐藏着不少“坑”,如果不注意,可能会导致性能问题、意外的错误容... 目录8个避坑点如下:1. 字符串的不可变性:每次修改都创建新对象2. 使用 == 比较字符串,陷阱满

Java判断多个时间段是否重合的方法小结

《Java判断多个时间段是否重合的方法小结》这篇文章主要为大家详细介绍了Java中判断多个时间段是否重合的方法,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录判断多个时间段是否有间隔判断时间段集合是否与某时间段重合判断多个时间段是否有间隔实体类内容public class D

IDEA编译报错“java: 常量字符串过长”的原因及解决方法

《IDEA编译报错“java:常量字符串过长”的原因及解决方法》今天在开发过程中,由于尝试将一个文件的Base64字符串设置为常量,结果导致IDEA编译的时候出现了如下报错java:常量字符串过长,... 目录一、问题描述二、问题原因2.1 理论角度2.2 源码角度三、解决方案解决方案①:StringBui

Java覆盖第三方jar包中的某一个类的实现方法

《Java覆盖第三方jar包中的某一个类的实现方法》在我们日常的开发中,经常需要使用第三方的jar包,有时候我们会发现第三方的jar包中的某一个类有问题,或者我们需要定制化修改其中的逻辑,那么应该如何... 目录一、需求描述二、示例描述三、操作步骤四、验证结果五、实现原理一、需求描述需求描述如下:需要在

Java中ArrayList和LinkedList有什么区别举例详解

《Java中ArrayList和LinkedList有什么区别举例详解》:本文主要介绍Java中ArrayList和LinkedList区别的相关资料,包括数据结构特性、核心操作性能、内存与GC影... 目录一、底层数据结构二、核心操作性能对比三、内存与 GC 影响四、扩容机制五、线程安全与并发方案六、工程

JavaScript中的reduce方法执行过程、使用场景及进阶用法

《JavaScript中的reduce方法执行过程、使用场景及进阶用法》:本文主要介绍JavaScript中的reduce方法执行过程、使用场景及进阶用法的相关资料,reduce是JavaScri... 目录1. 什么是reduce2. reduce语法2.1 语法2.2 参数说明3. reduce执行过程

如何使用Java实现请求deepseek

《如何使用Java实现请求deepseek》这篇文章主要为大家详细介绍了如何使用Java实现请求deepseek功能,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录1.deepseek的api创建2.Java实现请求deepseek2.1 pom文件2.2 json转化文件2.2

Java调用DeepSeek API的最佳实践及详细代码示例

《Java调用DeepSeekAPI的最佳实践及详细代码示例》:本文主要介绍如何使用Java调用DeepSeekAPI,包括获取API密钥、添加HTTP客户端依赖、创建HTTP请求、处理响应、... 目录1. 获取API密钥2. 添加HTTP客户端依赖3. 创建HTTP请求4. 处理响应5. 错误处理6.

Spring AI集成DeepSeek的详细步骤

《SpringAI集成DeepSeek的详细步骤》DeepSeek作为一款卓越的国产AI模型,越来越多的公司考虑在自己的应用中集成,对于Java应用来说,我们可以借助SpringAI集成DeepSe... 目录DeepSeek 介绍Spring AI 是什么?1、环境准备2、构建项目2.1、pom依赖2.2