首页 > 编程语言 >Java基础之 Integer 类源码分析

Java基础之 Integer 类源码分析

时间:2023-01-14 22:00:21浏览次数:47  
标签:Java int 源码 static 127 println Integer IntegerCache


Integer 类


源码说明

Java中Integer是基本数据类型int的包装类。也就是每一个Integer对象包含一个int类型的属性,是抽象类Number类的子类,位于java.lang包下。

部分源码:

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

@Native public static final int MIN_VALUE = 0x80000000;
@Native public static final int MAX_VALUE = 0x7fffffff;

private final int value;

public Integer(int value) {
this.value = value;
}

public Integer(String s) throws NumberFormatException {
this.value = parseInt(s, 10);
}
}

通过源码可以看出,最小值用十六进制表示为​​0x80000000​​​。使用二进制表示为​​1000 0000 0000 0000 0000 0000 0000 0000​​​.最大值用十六进制表示为​​0x7fffffff​​​。 使用二进制表示为 ​​0111 1111 1111 1111 1111 1111 1111 1111​​. 由于一个字节占用8位,而Integer的最大值和最小值都是占用32位,由此而知,int类型是占用4个字节的。最小值为 -2的31次方。最大值为2的31次方-1.

为何​​0x80000000​​可以表示最小值,可以参考这篇文章 ​​原码,反码和补码​​.

面试题

我们首先来看一道题:

public class IntegerTest {
public static void main(String[] args) {
Integer i1 = 127;
Integer i2 = new Integer(127);
Integer i3 = 127;
System.out.println(i1 == i2);
System.out.println(i1.equals(i2));
System.out.println(i1 == i3);
Integer ii1 = 128;
Integer ii2 = 128;
System.out.println(ii1 == ii2);
}
}

结果是:

false
true
true
false

不知道你们有没有答对呢?下面我们来看一下反编译过来的代码:

public class IntegerTest {
public static void main(String[] args) {
Integer i1 = Integer.valueOf(127);
Integer i2 = new Integer(127);
Integer i3 = Integer.valueOf(127);
System.out.println((i1 == i2));
System.out.println(i1.equals(i2));
System.out.println((i1 == i3));
Integer ii1 = Integer.valueOf(128);
Integer ii2 = Integer.valueOf(128);
System.out.println((ii1 == ii2));
}
}

可以看出 i1 和 i3 都调用了Integer.valueOf方法,这其实就是自动装箱的过程.Java基本数据自动转为包装类的过程称为自动装箱

我们看一下valueOf()方法的源码:

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

我们看到其中有一个IntegerCache类,我们再看一下源码:

private static class IntegerCache {
static final int low = -128;
static final int high;
static final Integer cache[];

static {
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);
h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
} catch( NumberFormatException nfe) {}
}
high = h;

cache = new Integer[(high - low) + 1];
int j = low;
for(int k = 0; k < cache.length; k++)
cache[k] = new Integer(j++);
assert IntegerCache.high >= 127;
}
private IntegerCache() {}
}

可以看出其中有一步是:​​sun.misc.vm.getSavedProperty("java.lang.Integer.IntegerCache.high")​​.

实际上在Java5中引入这个特性的时候,范围固定在-128到127之间。后来在Java6后,最大映射到 java.lang.Integer.IntegerCache.high, 可以使用JVM的启动参数设置最大值。(通过JVM的启动参数 -XX:AutoBoxCacheMax=size 修改)

我们暂时把自定义部分去掉,如下:

private static class IntegerCache {
static final int low = -128;
static final int high;
static final Integer cache[];

static {
int h = 127;
high = h;
cache = new Integer[(high - low) + 1];//127-(-128)+1=256
int j = low;
for(int k = 0; k < cache.length; k++)
cache[k] = new Integer(j++);
assert IntegerCache.high >= 127;
}

private IntegerCache() {}
}

以上就是Integer缓存池的源代码。为什么长度要加上1呢?因为需要把整数0算上。我们通过源码可以看到,对于Integer类型,内部有一个缓存池,实现把-128到127之间的对象先创建出来了。所以如果是通过调用valueOf()方法创建并且在-128到127之间的数,自动返回缓存中的对象,而由于自动装箱调用的就是valueOf方法。

下面我们再次来看下面的题:

Integer i1 = 127;
Integer i2 = new Integer(127);
Integer i3 = 127;
System.out.println(i1 == i2);//false
System.out.println(i1.equals(i2));//true
System.out.println(i1 == i3);//true
Integer ii1 = 128;
Integer ii2 = 128;
System.out.println(ii1 == ii2);//false

由于i1,i3 是自动装箱,调用了Integer.valueOf(),同时在默认返回下-128–127范围内,所以走的是缓存,故i1==i3为true (等于号是直接比较地址的)。而i2是自己创建对象,不是调用valueOf方法的,那么就不会走缓存,所以 i1 == i2 是false。那么为什么i1.equals(i2) 也会是true。我们看一下Integer类 valueOf()源码:

public boolean equals(Object obj) {
if (obj instanceof Integer) {
return value == ((Integer)obj).intValue();
}
return false;
}

通过源码可以看出,首先判断传入的是否为Integer 类型。

  • 是:判断其intValue()值与当前对象的value是否相等
  • 是:返回true。
  • 否:返回false。
  • 其他:返回false。

总结,使用Integer i = 9 这种方式来定义; 只要给值在-128到127之间,调用自动装箱方法valueOf,这个值都是作为下标处理的,取的是Integer缓存池的对象。

装箱拆箱过程

自动装箱:

Integer i = 2;

上面的代码会自动装箱。编译之后如下:

Integer i = Integer.valueOf(2);

自动拆箱:

int i = 2;
Integer ii = 2;
System.out.println(i == ii);

上面的代码经过自动拆箱。即编译之后变成如下:

int i = 2;
Integer ii = Integer.valueOf(2);
System.out.println(i == ii.intValue())

试题+1

在牛客上看到这样一个问题,如下:

【JAVA】条件 a == (Integer) 1 && a == (Integer) 2有可能为true吗?

代码可以表示为:

public static void main(String[] args){
int a = 1;
if(a == (Integer)1 && a == (Integer)2){
System.out.println("true");
}
}

乍一看,肯定不可能啊,但是仔细想想呢?

我们先看一下其反编译过来的代码:

int a = 1;
if (a == Integer.valueOf(1).intValue() && a == Integer.valueOf(2).intValue())
System.out.println(true);

可以看出来,​​a == (Integer)1​​ ,这句其实是先将1进行装箱,再进行拆箱与a进行比较。但是由于自动装箱走缓存同时1和2又在-128到127之间,所以我们可以从缓存进行入手。

Java基础之  Integer 类源码分析_java


如下:

public static void main(String[] args) throws Exception {
//1.获取Integer类的Class类对象
Class<Integer> integerClass = Integer.class;
//2.获取Integer类内部的类的对象,只有一个,取0,就是IntegerCache类
Class<?> integerCacheClass = integerClass.getDeclaredClasses()[0];
//3.获取IntegerCache类的cache属性对象
Field cacheField = integerCacheClass.getDeclaredField("cache");
//4.设置可访问
cacheField.setAccessible(true);
//5.由于IntegerCache类是static的,在加载的时候会走IntegerCache类中的static代码块
//把cache数组给初始化,所以我们后面是可以直接操作的
Integer[] o = (Integer[])cacheField.get(integerCacheClass);
//0 下标是示-128对象,127下标的是-1对象,128下标的是0对象,129和130下标的分别是1对象 和 2对象
//6. 将下标为130的2对象 更新为 下标为129的1对象.
o[130] = o[129];
System.out.println(Arrays.toString(o));
int a = 1;
//此时由于使用了类型装换,会进行自动装箱,再拆箱比较,所以1和2获取到的都是缓存中的1对象。再拆箱比较,就成立了。
if (a == (Integer)1 && a == (Integer)2){
System.out.println(true);
}
}

上面的代码即可在最后输入​​true​​. 主要是利用反射来修改IntegerCache的内容。如果前面自动装箱走缓存理解了,那么这里也可以理解,主要是配合反射来修改原来已经初始化好的缓存中的内容。

数组截图:

Java基础之  Integer 类源码分析_java_02

另:

byte类型占用1个字节。

short类型占用2个字节。

char类型占用2个字节。

int类型占用4个字节。

long类型占用8个字节。

float类型占用4个字节。7到8位有效数字。

double类型占用8个字节。16到17位有效数字。


标签:Java,int,源码,static,127,println,Integer,IntegerCache
From: https://blog.51cto.com/u_12131813/6007938

相关文章

  • java CountDownLatch用法 主线程等待子线程执行完后再执行
    这里记录一下下面这种情况:主线程需要等待多个子线程执行完后再执行。我们先看一下下面的场景:packagecom.java4all.mypoint;importjava.util.concurrent.CountDownLatch;/*......
  • java Integer值比较
    所有相同类型的包装类对象之间的值比较,应该使用equals方法比较。先看个简单实例:publicstaticvoidmain(String[]args)throwsException{Integera=-121;......
  • 有了这份Java面试中的葵花宝典,让你面试起飞!!!
    HashMap面试题HashMap与HashTable的区别1.HashMap线程不安全HashTable线程是安全的采用synchronized2.HashMap允许存放key为nullHashTable不允许存放key为null3.......
  • 在Linxu云服务器中创建Java版MC服务器
    前言因为学校的课程里有华为云的Linux课程,课程发放有2佰代金券用于开启云服务器,现在课程基本结束了,代金券还剩一百五十多,之前已经试过和舍友开过MC服务器,那趁现在结课的时......
  • Java集合 - ConcurrentHashMap
    介绍ConcurrentHashMap技术是为了解决问题而生的,ConcurrentHashMap解决了多个线程同时操作一个HashMap时,可能出现的内部问题。当多个线程同时操作一个HashMap时,有可......
  • Java集合 - ConcurrentHashMap
    介绍ConcurrentHashMap技术是为了解决问题而生的,ConcurrentHashMap解决了多个线程同时操作一个HashMap时,可能出现的内部问题。当多个线程同时操作一个HashMap时,有可......
  • Java 方法 实例 比较两个长方形的面积
    学习后,优化的,方法的目的一是可以重复被调用:packagecom.fqs.demo;publicclassMethodDemo2{publicstaticvoidmain(String[]args){//定义方法比......
  • Java判断是否是Windows系统
    在我们开发中,经常会读取一些本地文件,放到服务器肯定就要读取服务器的路径。本地win,远端服务器为Linux,所以就会进行系统判断等操作。//linux目录StringconfFileDir="......
  • java-打包
    方法0.使用package打包pom代码<build><plugins><plugin><groupId>org.springframework.boot</groupId>......
  • Java学习——酒店管理系统1
    酒店管理系统一、代码1.原始代码项目需求程序一启动,在控制台输出以下菜单用户选择1,2,3之后再一次展示菜单,可以一直选择用户选择4则提示谢谢您的使用程序运行......