探究Java中Integer缓冲区底层原理

目录
  • 一. Integer底层原理探究
    • 1. int和Integer的区别
    • 2. 被final修饰的Integer类
    • 3. IntegerCache缓冲区
    • 4. 几个核心实验
      • 4.1 比较new出的两个Integer对象
      • 4.2 Integer对象和int变量进行比较
      • 4.3 非new的Integer变量和new出的Integer变量进行比较
      • 4.4 两个非new生成的Integer对象进行比较
      • 4.5 ==和equals的区别
    • 5. 结论
  • 二. 结语

一. Integer底层原理探究

1. int和Integer的区别

除了要掌握Integer的用法之外,我们还要了解它的一些底层内容,因为在面试时,关于Integer的底层考察的比较多。比如一个常见的面试题是这样的:请问int和Integer的区别有哪些?

面对这样的一道题目,你该怎么回答?常规的答案其实很容易答出来,比如:

  • int是基本数据类型,代表整型数据,默认值是0;
  • Integer是 int的包装类,属于引用类型,默认值为 null
  • int 和 Integer 都可以表示某一个整型数值;
  • Integer变量实际是对象的引用,当new一个Integer时,实际上是生成一个指针指向此对象;而int则是直接存储数据值;
  • Integer可以区分出未赋值和值为 0 的区别,而int 则无法表达出未赋值的情况;
  • int 和 Integer 不能够互用,因为他们是两种不同的数据类型;
  • int在初始化时,可以直接写成 int=1 的形式;
  • 因为Integer是包装类型,使用时可以采用 Integer i = new Integer(1) 的形式,但因为Java中的自动装箱和拆箱机制,使得对Integer类的赋值也可以使用 Integer i= 1 的形式;
  • 如果我们只是进行一些加减乘除的运算 或者 作为参数进行传递,那么就可以直接使用int这样的基本数据类型;但如果想按照对象来进行操作处理,那么就要使用Integer来声明一个对象。

但是如果你只能回答出这样的答案,你在面试官的眼里只能算合格,还算不上优秀,我们需要对Integer了解地更多一些。

2. 被final修饰的Integer类

为了搞清楚Integer的底层,我们就不得不研究一下它的源码,我们来追踪一下Integer源码,如下图所示:

从源码中可以看出,Integer是Number的一个子类,且被final所修饰!请大家回顾一些之前讲过的final知识点。

我们知道,被final修饰的类是常量类,该类不能被继承,里面的方法不能被重写,创建出的对象也不能被修改!总之,Integer符合final类的特征。

3. IntegerCache缓冲区

我们还记得,在Integer中有一个valueOf()方法,该方法可以将int值转为Integer对象。接下来我们来看看该方法的实现源码,如下图所示:

从上图的源码截图中我们可以看到,Integer中有一个缓冲区叫做IntegerCache,这是Integer中的一个内部类,如下图所示:

我们可以看到,low就是-128,high等于127,这是缓冲区的最低和最高边界。那么这个缓冲区的存在到底有什么用呢?大家别着急,我们先做几个核心实验。

4. 几个核心实验

为了能够讲清楚Integer的底层逻辑,小编给大家设计了如下代码,用于验证Integer的底层设计。

4.1 比较new出的两个Integer对象

我们通过new对象的方式,来创建两个Integer对象i和j,并比较这两个对象。

//通过new生成的两个Integer变量进行比较,结果为false
Integer i = new Integer(100);
Integer j = new Integer(100);
System.out.print(i == j); //false

从运行的结果中可以看出,通过new生成的两个Integer对象永远是不会相等的。这是因为new生成的是两个对象,Integer变量实际上是对Integer对象的引用,这两个对象的内存地址是不同的。

4.2 Integer对象和int变量进行比较

接下来我们在把一个Integer对象和int变量进行比较,如下:

Integer i = new Integer(100);
int j = 100;
System.out.print(i == j); //true

Integer变量和int变量进行比较时,只要两个变量的值是相等的,结果就为true。这是因为Integer包装类和int基本类型进行比较时,Java会进行自动拆箱操作,将Integer转为了int,然后再进行比较,实际上就变为了两个int变量的比较。本案例中两者的值都是100,所以用“==”等号进行比较时自然就是相等的。

4.3 非new的Integer变量和new出的Integer变量进行比较

然后我们再把一个非new的Integer变量和new出的Integer变量进行比较,如下所示:

//非new生成的Integer变量和new Integer()生成的变量进行比较
Integer i = new Integer(100);
//自动装箱
Integer j = 100;
System.out.print(i == j); //false

在这段代码中,非new生成的Integer变量和new Integer()生成的变量进行比较时,结果却为false 这是因为非new生成Integer变量时,内部会调用valueOf()方法,进行自动装箱操作,此时会把Integer变量的值指向Java常量池中的数据。而new Integer()生成的变量,则指向的是堆中新建的对象,两者在内存中的地址是不同的。

4.4 两个非new生成的Integer对象进行比较

接着我们再对两个非new生成的Integer对象进行比较,如下所示:

//两个非new生成的Integer对象进行比较
//i与j的取值范围是在 -128~127 之间!
Integer i = 100;
Integer j = 100;
System.out.print(i == j); //true

//x与y的取值范围不在 -128~127 之间!
Integer x = 200;
Integer y = 200;
System.out.print(x == y); //false

这段代码中,两个非new生成的Integer对象进行比较时,如果两个变量的取值在 -128到127 之间,则比较结果为true;如果两个变量的值不在此区间,则比较结果为false。 这又是为什么呢? 其实要想弄明白这个原因,我们只需要看看Integer类的valueOf()方法是怎么写的就可以了。valueOf()方法源码如下:

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

我们知道,valueOf(int i)方法可以将int值自动装箱变成对应的Integer实例。 并且从这段源码中我们可以看到其内部有一个if判断,根据判断结果的不同,会有2种不同的方式得到Integer对象:当arg大于等于-128且小于等于127时,则直接从缓存中返回一个已经存在的对象;如果参数的值不在这个范围内,则new一个Integer对象返回,要么new Integer,要么从int常量池中获取

之前我们构建Integer对象的传统方式是直接 new 一个Integer对象,内部会调用构造器。但是根据实践,我们发现大部分的数据操作都是集中在有限的、较小的数值范围内。因而在JDK 1.5中,新增了一个静态工厂方法valueOf(int i)。当我们进行Integer i=xxx 赋值操作时,Java内部会调用执行这个valueOf()实现自动装箱。而在调用valueOf()方法时,其内部会利用缓存机制,对取值在-128~127之间的int值进行缓存操作,这是在JDK 1.5 之后进行的一个可以明显改善性能的提升 而按照Javadoc文档,该缓存机制默认会缓存在 -128 到 127 之间的值,不在该区间的值并不会进行缓存。所以,给Integer i赋值的大小不同,比较的结果也可能会不同。

4.5 ==和equals的区别

最后我们再做一个实验,来看看==与equals比较两个Integer对象时有什么不同。

Integer x = 127;
Integer y = 127;

Integer m = 100000;
Integer n = 100000;

System.out.println("x == y: " + (x==y)); // true
System.out.println("m == n: " + (m==n)); // false

System.out.println("x.equals(y): " + x.equals(y)); // true
System.out.println("m.equals(n): " + m.equals(n)); // true

从该实验中可以看出,==比较时,较小的两个相同的Integer会返回true,较大的两个相同的Integer会返回false。结合上面给大家的讲解,你思考一下这是为什么?

5. 结论

通过以上的几个核心实验,可以给大家梳理出一个结论:

当我们利用”==“等号比较两个Integer i 和 Integer j的值时,如果取值范围是在-128~127之间,两个相同的Integer值会返回true;如果不在该区间,两个相同的Integer值会返回false。这是因为Integer是final类,编译器把Integer i = 100; 自动变为Integer i = Integer.valueOf(100); 。为了节省内存, Integer.valueOf() 对于较小的数,始终会返回相同的实例对象,因此,==比较的结果就是true。

那么如果我们只是为了比较两个Integer对象的值是否相等,而不是为了比较两个对象的地址是否相同,在开发时请尽量使用equals()方法,而不是==!

并且我们现在还知道,在Java中有3种方式可以构造出一个Integer对象,代码如下:

//方法1:
Integer i = new Integer(100);
//方法2:
Integer i = Integer.valueOf(100);
//方法3:
Integer i = 100;

实际上,方法2和方法3的本质是一样的,所以开发时为了简洁,我们一般是通过方法3来得到一个Integer对象。但是尽量不要使用方法1来构建Integer对象,这是因为方法1总是会创建一个新的 Integer 实例,而方法2和方法3则会尽可能地返回缓存的实例对象,以节省内存。

所以最终关于”int和Integer的区别有哪些“这道面试题的答案,如果你想拿到高分,就需要把Integer的底层原理也回答出来才行!如果你可以把以上内容都回答清楚,我相信单凭这一道题目,就足以让面试官对你刮目相看!

二. 结语

最后我再梳理一下该问题的回答要点:

先简单回顾Java中的数据类型及取值范围;

然后简介基本类型与包装类,最后还能说明为什么需要有包装类;

接着说一下int与Integer的基本区别;

最后再说int与Integer的深入区别,即底层的源码和原理。

如果你可以把我总结的这4点都能回答好,就这一个问题,面试官就会对你留下深刻的影响,他就会认为你的基础知识足够扎实,因为大多数人只会回答int和Integer的基本区别,很少有人去回答底层的内容!而通过这个问题,面试官也会了解到,你对Java的内存分配是很熟悉的!

以上就是探究Java中Integer缓冲区底层原理的详细内容,更多关于Java Integer底层原理的资料请关注我们其它相关文章!

(0)

相关推荐

  • Java Integer如何获取第一位和最后一位,并截取

    目录 Integer获取第一位和最后一位并截取 场景 求Integer位数的巧妙方法 Integer获取第一位和最后一位并截取 场景 获取 5,10,15,25,30; 判断尾数为5的进入判断 public static void main(String[] args) {         String str = "数据测试,自定义字段";         //判断第一个字符是否为"数"         if (str.startsWith("数&quo

  • java如何将int数组转化为Integer数组

    目录 将int数组转化为Integer数组 Java int和Integer互转原理 Java Integer.int 与 new Integer() Integer.valueOf() new Integer() 为什么 Java 9 不建议使用 new Integer 了? int 与 integer 相互转换及区别 将int数组转化为Integer数组 这里使用java8的stream来进行转化,详细步骤如下所示: //初始化int数组 int[] nums = {1,2,3,4,5,6}

  • java两个integer数据判断相等用==还是equals

    目录 问题案例 原因分析 源码分析 解决方法 备注 问题案例 来个简单点的例子 public static void main(String[] args) { for (int i = 0; i < 150; i++) { Integer a = i; Integer b = i; System.out.println(i + " " + (a == b)); } } i取值从0到150,每次循环a与b的数值均相等,输出a == b.运行结果: 0 true 1 true 2

  • Java Big Number操作BigInteger及BigDecimal类详解

    目录 BigInteger类 构造函数 类方法 BigDecimal类 BigInteger类 java.math.BigInteger 类的使用场景是大整数操作.它提供类似所有Java的基本整数运算符和java.lang.Math中的所有相关的方法的操作,如+.-.*./.%.&.|.mod.>>.<<,以及min().max()等等. 只不过它操作的整数都是极其大的,为科学计算提供了很大的便利.比如下面的代码就是计算20000000000000000000 * 3000

  • 一场由Java中Integer引发的踩坑实战

    看过阿里巴巴开发手册的同学应该都会对Integer临界值127有点印象. 原文中写的是: [强制]所有整型包装类对象之间值的比较,全部使用 equals 方法比较. 说明:对于 Integer var = ? 在-128 至 127 之间的赋值,Integer 对象是在 IntegerCache.cache 产生, 会复用已有对象,这个区间内的 Integer 值可以直接使用==进行判断,但是这个区间之外的所有数据,都 会在堆上产生,并不会复用已有对象,这是一个大坑,推荐使用 equals 方法

  • 在java中ArrayList集合底层的扩容原理

    第一章 前言概述 第01节 概述 底层说明 ArrayList是List的实现类,它的底层是用Object数组存储,线程不安全 后期应用 适合用于频繁的查询工作,因为底层是数组,可以快速通过数组下标进行查找 第02节 区别 区别方向 ArrayList集合 LinkedList集合 线程安全 不安全 不安全 底层原理 Object类型数组 双向链表 随机访问 支持(实现 RandomAccess接口) 不支持 内存占用 ArrayList 浪费空间, 底层是数组,末尾预留一部分容量空间 Link

  • 一文带你弄懂Java中线程池的原理

    目录 为什么要用线程池 线程池的原理 ThreadPoolExecutor提供的构造方法 ThreadPoolExecutor的策略 线程池主要的任务处理流程 ThreadPoolExecutor如何做到线程复用的 四种常见的线程池 newCachedThreadPool newFixedThreadPool newSingleThreadExecutor newScheduledThreadPool 小结 在工作中,我们经常使用线程池,但是你真的了解线程池的原理吗?同时,线程池工作原理和底层实

  • 详解Java 中泛型的实现原理

    泛型是 Java 开发中常用的技术,了解泛型的几种形式和实现泛型的基本原理,有助于写出更优质的代码.本文总结了 Java 泛型的三种形式以及泛型实现原理. 泛型 泛型的本质是对类型进行参数化,在代码逻辑不关注具体的数据类型时使用.例如:实现一个通用的排序算法,此时关注的是算法本身,而非排序的对象的类型. 泛型方法 如下定义了一个泛型方法, 声明了一个类型变量,它可以应用于参数,返回值,和方法内的代码逻辑. class GenericMethod{ public <T> T[] sort(T[]

  • 详解Java volatile 内存屏障底层原理语义

    目录 一.volatile关键字介绍及底层原理 1.volatile的特性(内存语义) 2.volatile底层原理 二.volatile--可见性 三.volatile--无法保证原子性 四.volatile--禁止指令重排 1.指令重排 2.as-if-serial语义 五.volatile与内存屏障(Memory Barrier) 1.内存屏障(Memory Barrier) 2.volatile的内存语义实现 六.JMM对volatile的特殊规则定义 一.volatile关键字介绍及底

  • Java中枚举的实现原理介绍

    目录 基本概述 使用方式 条件选择 循环遍历 集合映射 常用方法 总结 基本概述 在 JDK1.5 之前,通过定义常量使用的都是:public static fianl.而枚举的设计,就是把相关的常量分组到一个枚举类型里,方便创建和管理. 比如我们要定义一个颜色常量: public enum Colour { RED, YELLOW, BLUE, GREEN } 这段代码通过底层编译后,实际创建了4个枚举对象: new Enum<EnumTest>("RED", 0); n

  • 详解Java中AC自动机的原理与实现

    目录 简介 工作过程 数据结构 初始化 构建字典树 构建失败指针 匹配 执行结果 简介 AC自动机是一个多模式匹配算法,在模式匹配领域被广泛应用,举一个经典的例子,违禁词查找并替换为***.AC自动机其实是Trie树和KMP 算法的结合,首先将多模式串建立一个Tire树,然后结合KMP算法前缀与后缀匹配可以减少不必要比较的思想达到高效找到字符串中出现的匹配串. 如果不知道什么是Tire树,可以先查看:详解Java中字典树(Trie树)的图解与实现 如果不知道KMP算法,可以先查看:详解Java中

  • 详解Java中跳跃表的原理和实现

    目录 一.跳跃表的引入 二.算法分析 1.时间复杂度 2.空间复杂度 三.跳跃表介绍 四.跳跃表的实现 1.数据结构定义 2.查找 3.插入 4.删除 五.实战 1.代码 2.测试结果 一.跳跃表的引入 对有序顺序表可以采用二分查找,查找的时间复杂度为O (logn),插入.删除的时间复杂度为 O(n ).但是对有序链表不可以采用二分查找,查找.插入和删除的时间复杂度均为O (n). 有序链表如下图所示,若查找 8,则必须从第 1 个节点开始,依次比较 8 次才能查找成功. 如何利用链表的有序性

  • Java中GC的工作原理详细介绍

    Java中GC的工作原理 引子:面试时被问到垃圾回收机制,只是粗略的讲'程序员不能直接对内存操作,jvm负责对已经超过作用域的对象回收处理',面官表情呆滞,也就没再继续深入. 转文: 一个优秀的Java程序员必须了解GC的工作原理.如何优化GC的性能.如何与GC进行有限的交互,有一些应用程序对性能要求较高,例如嵌入式系统.实时系统等,只有全面提升内存的管理效率,才能提高整个应用程序的性能.本文将从GC的工作原理.GC的几个关键问题进行探讨,最后提出一些Java程序设计建议,如何从GC角度提高Ja

  • java 中volatile和lock原理分析

    java 中volatile和lock原理分析 volatile和lock是Java中用于线程协同同步的两种机制. Volatile volatile是Java中的一个关键字,它的作用有 保证变量的可见性 防止重排序 保证64位变量(long,double)的原子性读写 volatile在Java语言规范中规定的是 The Java programming language allows threads to access shared variables (§17.1). As a rule,

  • java中注解机制及其原理的详解

    java中注解机制及其原理的详解 什么是注解 注解也叫元数据,例如我们常见的@Override和@Deprecated,注解是JDK1.5版本开始引入的一个特性,用于对代码进行说明,可以对包.类.接口.字段.方法参数.局部变量等进行注解.它主要的作用有以下四方面: 生成文档,通过代码里标识的元数据生成javadoc文档. 编译检查,通过代码里标识的元数据让编译器在编译期间进行检查验证. 编译时动态处理,编译时通过代码里标识的元数据动态处理,例如动态生成代码. 运行时动态处理,运行时通过代码里标识

随机推荐