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

相关文章

JVM 的类初始化机制

前言 当你在 Java 程序中new对象时,有没有考虑过 JVM 是如何把静态的字节码(byte code)转化为运行时对象的呢,这个问题看似简单,但清楚的同学相信也不会太多,这篇文章首先介绍 JVM 类初始化的机制,然后给出几个易出错的实例来分析,帮助大家更好理解这个知识点。 JVM 将字节码转化为运行时对象分为三个阶段,分别是:loading 、Linking、initialization

Spring Security 基于表达式的权限控制

前言 spring security 3.0已经可以使用spring el表达式来控制授权,允许在表达式中使用复杂的布尔逻辑来控制访问的权限。 常见的表达式 Spring Security可用表达式对象的基类是SecurityExpressionRoot。 表达式描述hasRole([role])用户拥有制定的角色时返回true (Spring security默认会带有ROLE_前缀),去

浅析Spring Security认证过程

类图 为了方便理解Spring Security认证流程,特意画了如下的类图,包含相关的核心认证类 概述 核心验证器 AuthenticationManager 该对象提供了认证方法的入口,接收一个Authentiaton对象作为参数; public interface AuthenticationManager {Authentication authenticate(Authenti

Spring Security--Architecture Overview

1 核心组件 这一节主要介绍一些在Spring Security中常见且核心的Java类,它们之间的依赖,构建起了整个框架。想要理解整个架构,最起码得对这些类眼熟。 1.1 SecurityContextHolder SecurityContextHolder用于存储安全上下文(security context)的信息。当前操作的用户是谁,该用户是否已经被认证,他拥有哪些角色权限…这些都被保

Spring Security基于数据库验证流程详解

Spring Security 校验流程图 相关解释说明(认真看哦) AbstractAuthenticationProcessingFilter 抽象类 /*** 调用 #requiresAuthentication(HttpServletRequest, HttpServletResponse) 决定是否需要进行验证操作。* 如果需要验证,则会调用 #attemptAuthentica

Spring Security 从入门到进阶系列教程

Spring Security 入门系列 《保护 Web 应用的安全》 《Spring-Security-入门(一):登录与退出》 《Spring-Security-入门(二):基于数据库验证》 《Spring-Security-入门(三):密码加密》 《Spring-Security-入门(四):自定义-Filter》 《Spring-Security-入门(五):在 Sprin

Java架构师知识体认识

源码分析 常用设计模式 Proxy代理模式Factory工厂模式Singleton单例模式Delegate委派模式Strategy策略模式Prototype原型模式Template模板模式 Spring5 beans 接口实例化代理Bean操作 Context Ioc容器设计原理及高级特性Aop设计原理Factorybean与Beanfactory Transaction 声明式事物

Java进阶13讲__第12讲_1/2

多线程、线程池 1.  线程概念 1.1  什么是线程 1.2  线程的好处 2.   创建线程的三种方式 注意事项 2.1  继承Thread类 2.1.1 认识  2.1.2  编码实现  package cn.hdc.oop10.Thread;import org.slf4j.Logger;import org.slf4j.LoggerFactory

基本知识点

1、c++的输入加上ios::sync_with_stdio(false);  等价于 c的输入,读取速度会加快(但是在字符串的题里面和容易出现问题) 2、lower_bound()和upper_bound() iterator lower_bound( const key_type &key ): 返回一个迭代器,指向键值>= key的第一个元素。 iterator upper_bou

JAVA智听未来一站式有声阅读平台听书系统小程序源码

智听未来,一站式有声阅读平台听书系统 🌟&nbsp;开篇:遇见未来,从“智听”开始 在这个快节奏的时代,你是否渴望在忙碌的间隙,找到一片属于自己的宁静角落?是否梦想着能随时随地,沉浸在知识的海洋,或是故事的奇幻世界里?今天,就让我带你一起探索“智听未来”——这一站式有声阅读平台听书系统,它正悄悄改变着我们的阅读方式,让未来触手可及! 📚&nbsp;第一站:海量资源,应有尽有 走进“智听