Java工具类——包装类

我们都知道,JDK 其实给我们提供了很多很多 Java 开发者已经写好的现成的类,他们其实都可以理解成工具类,比如我们常见的集合类,日期相关的类,数学相关的类等等,有了这些工具类,你会发现它能很大程度的帮你节省时间,能很方便的实现你的需求。当然,没有这些包,你也能实现你的需求,但是你需要时间,今天我们主要是来学习一下包装类。

一、包装类介绍

1、为什么需要包装类?

我们知道 Java 语言是一个面向对象的编程语言,但是 Java 中的基本数据类型却不是面向对象的,但是我们在实际使用中经常需要将基本数据类型转换成对象,便于操作,比如,集合的操作中,这时,我们就需要将基本类型数据转化成对象,所以就出现了包装类。

2、包装类是什么呢?

包装类,顾名思义就是将什么经过包装的类,那么是将什么包装起来的呢,显然这里是将基本类型包装起来的类。包装类的作用就是将基本类型转成对象,将基本类型作为对象来处理。

Java 中我们知道,基本数据类型有8个,所以对应的包装类也是8个,包装类就是基本类型名称首字母大写。但Integer 和 Character 例外,它们显示全称,如下面表格所示:

基本数据类型 对应包装类
byte Byte
short Short
int Integer
long Long
float Float
double Double
char Character
boolean Boolean

二、包装类的继承关系

通过阅读 Java8 的 API 官方文档或者看源代码我们可以得知8个包装类的继承关系如下:

通过以上的继承关系图,我们其实可以这样记忆,包装类里面有6个与数字相关的都是继承自 Number 类,而其余两个不是与数字相关的都是默认继承 Object 类。通过看 API 官方文档,我们还可以得知这8个包装类都实现了Serializable , Comparable 接口。比如下图的 Integer 类

public final class Integer extends Number implements Comparable<Integer> {}

三、包装类的使用方法(基本操作)

接下来关于包装类的讲解我就讲Integer包装类,其他的都依此类推,用法和操作都是差不多的,只是名字不一样而已。

1、包装类的构造方法

8个包装类都有带自己对应类型参数的构造方法,其中8个包装类中除了Character还有构造方法重载,参数是String类型的。

Integer one = new Integer(666);
Integer two = new Integer("666");

2、包装类的自动拆装箱

在了解自动拆装箱之前,我们得先知道什么是拆箱和装箱。其实拆装箱主要应对基本类型与包装类型的相互转换问题。

  • 装箱:将基本类型转换成包装类型的过程叫做装箱。

  • 拆箱:将包装类型转换成基本类型的过程叫做拆箱。

其实,在 JDK1.5 版本之前,是没有自动拆装箱的,开发人员要手动进行装拆箱:

//手动装箱,也就是将基本类型10转换为引用类型
Integer integer = new Integer(10);
//或者
Integer integer1 = Integer.valueOf(10); //手动拆箱,也就是将引用类型转换为基本类型
int num = integer.intValue();

而在在 JDK1.5 版本之后,为了减少开发人员的工作,提供了自动装箱与自动拆箱的功能。实现了自动拆箱和自动装箱,如下方代码所示:

//自动装箱
Integer one = 1;
//自动拆箱
int two = one + 10;

其实以上两种方式本质上是一样得,只不过一个是自动实现了,一个是手动实现了。至于自动拆装箱具体怎么实现的我这里不做深入研究。

四、包装类的缓存机制

我们首先来看看以下代码,例1:

public static void main(String[] args) {
Integer i1 = 100;
Integer i2 = 100;
Integer i3 = new Integer(100);
Integer i4 = new Integer(100);
System.out.println(i1 == i2);//true
System.out.println(i1 == i3);//false
System.out.println(i3 == i4);//false
System.out.println(i1.equals(i2));//true
System.out.println(i1.equals(i3));//true
System.out.println(i3.equals(i4));//true
}

当我们修改了值为200的时候,例2:

public static void main(String[] args) {
Integer i1 = 200;
Integer i2 = 200;
Integer i3 = new Integer(200);
Integer i4 = new Integer(200);
System.out.println(i1 == i2);//false
System.out.println(i1 == i3);//false
System.out.println(i3 == i4);//false
System.out.println(i1.equals(i2));//true
System.out.println(i1.equals(i3));//true
System.out.println(i3.equals(i4));//true
}

通过上面两端代码,我们发现修改了值,第5行代码的执行结果竟然发生了改变,为什么呢?首先,我们需要明确第1行和第2行代码实际上是实现了自动装箱的过程,也就是自动实现了 Integer.valueOf 方法,其次,比较的是地址,而 equals 比较的是值(这里的 eauals 重写了,所以比较的是具体的值),所以显然最后五行代码的执行结果没有什么疑惑的。既然比较的是地址,例1的第5行代码为什么会是true呢,这就需要我们去了解包装类的缓存机制。

其实看Integer类的源码我们可以发现在第780行有一个私有的静态内部类,如下:

private static class IntegerCache {
static final int low = -128;
static final int high;
static final Integer cache[]; static {
// high value may be configured by property
int h = 127;
String integerCacheHighPropValue =
sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
if (integerCacheHighPropValue != null) {
try {
int i = parseInt(integerCacheHighPropValue);
i = Math.max(i, 127);
// Maximum array size is Integer.MAX_VALUE
h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
} catch( NumberFormatException nfe) {
// If the property cannot be parsed into an int, ignore it.
}
}
high = h; cache = new Integer[(high - low) + 1];
int j = low;
for(int k = 0; k < cache.length; k++)
cache[k] = new Integer(j++); // range [-128, 127] must be interned (JLS7 5.1.7)
assert IntegerCache.high >= 127;
} private IntegerCache() {}
}

我们知道,静态的内部类是在整个 Integer 加载的时候就已经加载完成了,以上代码初始化了一个 Integer 类型的叫 cache 的数组,取值范围是[-128, 127]。缓存机制的作用就是提前实例化相应范围数值的包装类对象,只要创建处于缓存范围的对象,就使用已实例好的对象。从而避免重复创建多个相同的包装类对象,提高了使用效率。如果我们用的对象范围在[-128, 127]之内,就直接去静态区找对应的对象,如果用的对象的范围超过了这个范围,会帮我们创建一个新的 Integer 对象,其实下面的源代码就是这个意思:

public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}

所以 例1 代码里,i1 和i2 是100,值的范围在[-128, 127],所以直接区静态区找,所以i1和i2指向的地址是同一个,所以 i1==i2;而在例2的代码里,i1 和i2 是200,值的范围不在在[-128, 127],所以分别创建了一个新的对象,放在了堆内存里,各自指向了不同的地址,所以地址都不同了,自然 i1 不等于 i2。

通过分析源码我们可以发现,只有 double 和 float 的自动装箱代码没有使用缓存,每次都是 new 新的对象,其它的6种基本类型都使用了缓存策略。

使用缓存策略是因为,缓存的这些对象都是经常使用到的(如字符、-128至127之间的数字),防止每次自动装箱都创建一次对象的实例。

五、包装类和基本数据类型的区别

  • 默认值不同

包装类的默认值是null,而基本数据类型是对应的默认值(比如整型默认值是0,浮点型默认值是0.0)

  • 存储区域不同

基本数据类型是把值保存在栈内存里,包装类是把对象放在堆中,然后通过对象的引用来调用他们

  • 传递方式不同

基本数据类型变量空间里面存储的是值,传递的也是值,一个改变,另外一个不变,而包装类属于引用数据类型,变量空间存储的是地址(引用),传递的也是引用,一个变,另外一个跟着变。

五、小结

​ 以上就是我对于Java包装类的个人理解,其实学习这些工具类还有一个更好的学习方式,就是去看官方文档(API官方文档地址:https://docs.oracle.com/javase/8/docs/api/)


公众号:良许Linux

有收获?希望老铁们来个三连击,给更多的人看到这篇文章

Java工具类—包装类的更多相关文章

  1. Java工具类之:包装类

    Java工具类--包装类 我们都知道,JDK 其实给我们提供了很多很多 Java 开发者已经写好的现成的类,他们其实都可以理解成工具类,比如我们常见的集合类,日期相关的类,数学相关的类等等,有了这些工 ...

  2. Java工具类——数学相关的类

    Java工具类--数学相关的类 在上一篇文章中,我们系统学习了 Java 里面的包装类,那么这篇文章,我们就来学习一下Java提供好的类--数学相关的类. 一.数学类介绍 在最早期学习 Java 基础 ...

  3. java工具类系列 (四.SerializationUtils)

    java工具类系列 (四.SerializationUtils) SerializationUtils该类为序列化工具类,也是lang包下的工具,主要用于序列化操作 import java.io.Se ...

  4. Java工具类——通过配置XML验证Map

    Java工具类--通过配置XML验证Map 背景 在JavaWeb项目中,接收前端过来的参数时通常是使用我们的实体类进行接收的.但是呢,我们不能去决定已经搭建好的框架是怎么样的,在我接触的框架中有一种 ...

  5. 排名前 16 的 Java 工具类

    在Java中,工具类定义了一组公共方法,这篇文章将介绍Java中使用最频繁及最通用的Java工具类.以下工具类.方法按使用流行度排名,参考数据来源于Github上随机选取的5万个开源项目源码. 一. ...

  6. 排名前16的Java工具类

    原文:https://www.jianshu.com/p/9e937d178203 在Java中,工具类定义了一组公共方法,这篇文章将介绍Java中使用最频繁及最通用的Java工具类.以下工具类.方法 ...

  7. 第一章 Java工具类目录

    在这一系列博客中,主要是记录在实际开发中会常用的一些Java工具类,方便后续开发中使用. 以下的目录会随着后边具体工具类的添加而改变. 浮点数精确计算 第二章 Java浮点数精确计算 crc32将任意 ...

  8. java工具类之按对象中某属性排序

    import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang ...

  9. 干货:排名前16的Java工具类

    在Java中,工具类定义了一组公共方法,这篇文章将介绍Java中使用最频繁及最通用的Java工具类.以下工具类.方法按使用流行度排名,参考数据来源于Github上随机选取的5万个开源项目源码. 一. ...

随机推荐

  1. cisco-GNS3-pix防火墙基本配置实操(持续更新)

    一.ASA和PIX基础配置 1.ASA防火墙配置 1.GNS配置 因为使用的GNS3的版本可能不同,gns配置asa防火墙的步骤可能不同 在低版本的gns中直接在qemu选项里可以直接配置,参考:ht ...

  2. Java实现 LeetCode 564 寻找最近的回文数(今天要GG在这道题了 头晕+题难(((φ(◎ロ◎;)φ))))

    564. 寻找最近的回文数 给定一个整数 n ,你需要找到与它最近的回文数(不包括自身). "最近的"定义为两个整数差的绝对值最小. 示例 1: 输入: "123&quo ...

  3. Java实现蓝桥杯算法提高 陶陶摘苹果

    试题 算法提高 陶陶摘苹果 资源限制 时间限制:1.0s 内存限制:256.0MB 问题描述 陶陶家的院子里有一棵苹果树,每到秋天树上就会结出n个苹果.苹果成熟的时候,陶陶就会跑去摘苹果.陶陶有个30 ...

  4. Java实现 蓝桥杯VIP 算法训练 连接字符串

    算法训练 连接字符串 时间限制:1.0s 内存限制:512.0MB 编程将两个字符串连接起来.例如country与side相连接成为countryside. 输入两行,每行一个字符串(只包含小写字母, ...

  5. Java实现 LeetCode 36 有效的数独

    36. 有效的数独 判断一个 9x9 的数独是否有效.只需要根据以下规则,验证已经填入的数字是否有效即可. 数字 1-9 在每一行只能出现一次. 数字 1-9 在每一列只能出现一次. 数字 1-9 在 ...

  6. SQL server 常用的数据库 DDL语言

    use (数据库名) //切换到目标数据库 if exists (select * from sysdatabases where name='数据库名') //如果括号里面是查看有没有这个数据库 d ...

  7. Java实现 洛谷 P1010 幂次方

    输入输出样例 输入 #1 1315 输出 #1 2(2(2+2(0))+2)+2(2(2+2(0)))+2(2(2)+2(0))+2+2(0) import java.util.Scanner; pu ...

  8. java实现第四届蓝桥杯连续奇数和

    连续奇数和 题目描述 小明看到一本书上写着:任何数字的立方都可以表示为连续奇数的和. 比如: 2^3 = 8 = 3 + 5 3^3 = 27 = 7 + 9 + 11 4^3 = 64 = 1 + ...

  9. Java并发编程实战总结 (一)

    前提 首先该场景是一个酒店开房的业务.为了朋友们阅读简单,我把业务都简化了. 业务:开房后会添加一条账单,添加一条房间排期记录,房间排期主要是为了房间使用的时间不冲突.如:账单A,使用房间1,使用时间 ...

  10. CentOS8.1操作系下使用通用二进制包安装MySQL8.0(实践整理自MySQL官方)

    写在前的的话: 在IT技术日新月异的今天,老司机也可能在看似熟悉的道路上翻车,甚至是大型翻车现场!自己一个人开车过去翻个车不可怕,可怕的是带着整个团队甚至是整个公司一起翻车山崖下,解决办法就是:新出现 ...