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

相关文章

Spring Boot 整合 SSE的高级实践(Server-Sent Events)

《SpringBoot整合SSE的高级实践(Server-SentEvents)》SSE(Server-SentEvents)是一种基于HTTP协议的单向通信机制,允许服务器向浏览器持续发送实... 目录1、简述2、Spring Boot 中的SSE实现2.1 添加依赖2.2 实现后端接口2.3 配置超时时

Spring Boot读取配置文件的五种方式小结

《SpringBoot读取配置文件的五种方式小结》SpringBoot提供了灵活多样的方式来读取配置文件,这篇文章为大家介绍了5种常见的读取方式,文中的示例代码简洁易懂,大家可以根据自己的需要进... 目录1. 配置文件位置与加载顺序2. 读取配置文件的方式汇总方式一:使用 @Value 注解读取配置方式二

一文详解Java异常处理你都了解哪些知识

《一文详解Java异常处理你都了解哪些知识》:本文主要介绍Java异常处理的相关资料,包括异常的分类、捕获和处理异常的语法、常见的异常类型以及自定义异常的实现,文中通过代码介绍的非常详细,需要的朋... 目录前言一、什么是异常二、异常的分类2.1 受检异常2.2 非受检异常三、异常处理的语法3.1 try-

Java中的@SneakyThrows注解用法详解

《Java中的@SneakyThrows注解用法详解》:本文主要介绍Java中的@SneakyThrows注解用法的相关资料,Lombok的@SneakyThrows注解简化了Java方法中的异常... 目录前言一、@SneakyThrows 简介1.1 什么是 Lombok?二、@SneakyThrows

Java中字符串转时间与时间转字符串的操作详解

《Java中字符串转时间与时间转字符串的操作详解》Java的java.time包提供了强大的日期和时间处理功能,通过DateTimeFormatter可以轻松地在日期时间对象和字符串之间进行转换,下面... 目录一、字符串转时间(一)使用预定义格式(二)自定义格式二、时间转字符串(一)使用预定义格式(二)自

Spring 请求之传递 JSON 数据的操作方法

《Spring请求之传递JSON数据的操作方法》JSON就是一种数据格式,有自己的格式和语法,使用文本表示一个对象或数组的信息,因此JSON本质是字符串,主要负责在不同的语言中数据传递和交换,这... 目录jsON 概念JSON 语法JSON 的语法JSON 的两种结构JSON 字符串和 Java 对象互转

JAVA保证HashMap线程安全的几种方式

《JAVA保证HashMap线程安全的几种方式》HashMap是线程不安全的,这意味着如果多个线程并发地访问和修改同一个HashMap实例,可能会导致数据不一致和其他线程安全问题,本文主要介绍了JAV... 目录1. 使用 Collections.synchronizedMap2. 使用 Concurren

Java Response返回值的最佳处理方案

《JavaResponse返回值的最佳处理方案》在开发Web应用程序时,我们经常需要通过HTTP请求从服务器获取响应数据,这些数据可以是JSON、XML、甚至是文件,本篇文章将详细解析Java中处理... 目录摘要概述核心问题:关键技术点:源码解析示例 1:使用HttpURLConnection获取Resp

Java的栈与队列实现代码解析

《Java的栈与队列实现代码解析》栈是常见的线性数据结构,栈的特点是以先进后出的形式,后进先出,先进后出,分为栈底和栈顶,栈应用于内存的分配,表达式求值,存储临时的数据和方法的调用等,本文给大家介绍J... 目录栈的概念(Stack)栈的实现代码队列(Queue)模拟实现队列(双链表实现)循环队列(循环数组

Java中Switch Case多个条件处理方法举例

《Java中SwitchCase多个条件处理方法举例》Java中switch语句用于根据变量值执行不同代码块,适用于多个条件的处理,:本文主要介绍Java中SwitchCase多个条件处理的相... 目录前言基本语法处理多个条件示例1:合并相同代码的多个case示例2:通过字符串合并多个case进阶用法使用