Integer是平时开发中最常用的类之一,但是如果没有研究过源码很多特性和坑可能就不知道,下面深入源码来分析一下Integer的设计和实现。

Integer:

  继承结构:

  -java.lang.Object

    --java.lang.Number

      ---java.lang.Integer

  其中父类Number是个抽象类,是所有数字类型相关的类的父类,例如DoubleFloatIntegerLongShort。

  Integer类还实现了Comparable接口用以比较两个Integer的大小。

//源码
public final class Integer extends Number implements Comparable<Integer>

  Integer类中规定了范围大小时在-2^31~2^31-1之间。

    //源码
/**
* A constant holding the minimum value an {@code int} can
* have, -2<sup>31</sup>.
*/
@Native public static final int MIN_VALUE = 0x80000000; /**
* A constant holding the maximum value an {@code int} can
* have, 2<sup>31</sup>-1.
*/
@Native public static final int MAX_VALUE = 0x7fffffff;

  另外还有用来以二进制补码形式表示 int 值的比特位数的SIZE字段,表示基本类型 intClass 实例的TYPE字段。

  内部方法实现:

  Integer大概实现了四五十个方法,下面结合源码分析一下平时常用又比较重要的几个方法。

  首先构造一个Integer对象,Integer的构造方法非常简单直接传入一个int或者string即可。传入int是直接赋值给value字段保存。传入string是先把s通过parseInt方法转换成十进制int再赋值给value字段。

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

  接下来看一下这个不简单的parseInt方法。

  从方法签名就可以看出这个方法的作用是把传入的字符串s解析单做radix机制的字串来解析成十进制int值。并进行了一些异常处理。举个栗子:

     parseInt("0", 10) returns 0
parseInt("473", 10) returns 473
parseInt("+42", 10) returns 42
parseInt("-0", 10) returns 0
parseInt("-FF", 16) returns -255
parseInt("1100110", 2) returns 102
parseInt("2147483647", 10) returns 2147483647
parseInt("-2147483648", 10) returns -2147483648
parseInt("2147483648", 10) throws a NumberFormatException
parseInt("99", 8) throws a NumberFormatException
parseInt("Kona", 10) throws a NumberFormatException
parseInt("Kona", 27) returns 411787

  下面来看一下具体实现(为了更清楚的分析实现过程,文字都作为注释写在源代码里了):

  //源码,限于篇幅简化了源码格式。
  public static int parseInt(String s, int radix) throws NumberFormatException {
//这里有这个警告是因为valueOf方法使用了parseInt方法和IntegerCache对象,
//因为valueOf在IntegerCache初始化之前使用导致异常情况。后面会详细分析。
/*
* WARNING: This method may be invoked early during VM initialization
* before IntegerCache is initialized. Care must be taken to not use
* the valueOf method.
*/ //下面三个if用来判断参数是否合法。radix大小在2~36之间。
if (s == null) {
throw new NumberFormatException("null");
}
if (radix < Character.MIN_RADIX) {
throw new NumberFormatException("radix " + radix + " less than Character.MIN_RADIX");
}
if (radix > Character.MAX_RADIX) {
throw new NumberFormatException("radix " + radix + " greater than Character.MAX_RADIX");
} int result = 0; //解析结果
boolean negative = false; //是否是负数
int i = 0, len = s.length(); //索引变量和字符串长度
int limit = -Integer.MAX_VALUE; //最大值限制
int multmin; //基数下的最小值
int digit; //记录每一位的数字 if (len > 0) {
char firstChar = s.charAt(0);
if (firstChar < '0') { // 判断是否带‘+’或‘-’
if (firstChar == '-') {
negative = true;
limit = Integer.MIN_VALUE;
} else if (firstChar != '+')
throw NumberFormatException.forInputString(s); if (len == 1) // 格式非法,含有除了‘+’‘-’之外的字符。
throw NumberFormatException.forInputString(s);
i++;
}
multmin = limit / radix;
while (i < len) {
//利用了Character类中的digit非法,作用是解析一个字符。
digit = Character.digit(s.charAt(i++),radix);
//进行异常判断。
//这个解析字符串为数字的算法和平时想到的不太一样,是从字符串左边开始,初始化结果是0,
//其实是把结果算成负的,返回的时候再转回来。result -= digit;
if (digit < 0) {
throw NumberFormatException.forInputString(s);
}
if (result < multmin) {
throw NumberFormatException.forInputString(s);
}
result *= radix;
if (result < limit + digit) {
throw NumberFormatException.forInputString(s);
}
result -= digit;
}
} else {
throw NumberFormatException.forInputString(s);
}
return negative ? result : -result; //如果是负的就直接返回,因为算出来的已经是负数。
}

  平时经常使用的Integer.parseInt(String s)也是基于这个方法实现的。只不过默认radix为10.

  //源码
  public static int parseInt(String s) throws NumberFormatException {
return parseInt(s,10);
}

  接下来就来分析一下上面提到的,valueOf方法。一共有三个valueOf方法,只是传参不同。其中有两个的内部实现是依据valueOf(int i)和parseInt(String s, int radix)来实现的。

//源码
public static Integer valueOf(String s, int radix) throws NumberFormatException {
return Integer.valueOf(parseInt(s,radix));
}
public static Integer valueOf(String s) throws NumberFormatException {
return Integer.valueOf(parseInt(s, 10));
}

那就来分析一下valueOf(int i)方法就好了。

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

可以看到这里使用到了IntegerCache缓存,IntegerCache默认缓存-128~127之间的Integer。IntegerCache是Integer类的静态内部类。

//源码
private static class IntegerCache {
static final int low = -128; //默认low=-128
static final int high; //high可以配置,通过 VM 参数-XX:AutoBoxCacheMax=<size>
//high可以配置,所以默认缓存-128~127,但是也可以缓存另外的常用数。
static final Integer cache[]; //缓存数组 //静态代码块,Integer类加载时就缓存。
static {
// high value may be configured by property
int h = 127; //默认127
String integerCacheHighPropValue =
sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high"); //读取VM参数配置。
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; //保证[-128, 127]在缓存范围内。
} private IntegerCache() {}
}

下面看一段测试代码:

  
  //首先要明确一点,对象之间的==是比较内存地址,常数之间的比较是数值比较。
  public static void main(String[] args) {
Integer num1 = new Integer(100);
Integer num2 = new Integer(100);
System.out.println(num1 == num2);//false,因为这两个对象是独立创建的,有自己的内存空间和地址。
Integer num3 = 100;
Integer num4 = 100;
System.out.println(num3 == num4);//true,常数之间比较数值。
Integer num5 = 128;
Integer num6 = 128;
System.out.println(num5 == num6);//false,自动装箱成对象,但是超过了默认的缓存范围,同第一个。如果是127就是true。
Integer num7 = 100;
Integer num8 = new Integer(100);
System.out.println(num7 == num8);//false,两个对象之间比较内存地址,不同的是num7通过自动装箱调用valueOf方法,指向缓存的100,而num8是指向自己内存空间里的100.
int num9 = 100;
Integer num10 = new Integer(100);
     System.out.println(num9 == num10);//true,Integer对象和int比较时,Integer会自动拆箱(intValue方法)成为int,变成两个数值比较。
Integer num11 = 100;
System.out.println(num9 == num11);//true,num11通过自动装箱调用valueOf方法指向缓存中的100,比较的时候缓存中的100对象自动拆箱成为数值100.
}

  如果没有认真研究过Integer的缓存机制和自动拆箱装箱机制的话,这个程序的运行结果绝对会让你出乎意料。理解之后就OK了。

  理解这个缓存机制也是非常重要的,因为如果程序中因为这个出现了bug那么如果不知道缓存机制估计到死也调不出来。

  这里说一下关于Long,Short是和Integer机制类似,只不过不支持high的配置。Double,Float是没有缓存机制的,因为即使是-128~127之间的浮点数接近无穷大。

  这一次的Integer类的源码分析就到这里,Integer类里还有一些关于反码、补码计算等位运算的方法。如果有兴趣或者开发中用到再来研究。

JDK源码分析-Integer的更多相关文章

  1. JDK源码分析 – Integer

    Integer类的申明 public final class Integer extends Number implements Comparable<Integer> { … } Int ...

  2. JDK源码分析—— ArrayBlockingQueue 和 LinkedBlockingQueue

    JDK源码分析—— ArrayBlockingQueue 和 LinkedBlockingQueue 目的:本文通过分析JDK源码来对比ArrayBlockingQueue 和LinkedBlocki ...

  3. JDK 源码分析(4)—— HashMap/LinkedHashMap/Hashtable

    JDK 源码分析(4)-- HashMap/LinkedHashMap/Hashtable HashMap HashMap采用的是哈希算法+链表冲突解决,table的大小永远为2次幂,因为在初始化的时 ...

  4. 【JDK】JDK源码分析-HashMap(1)

    概述 HashMap 是 Java 开发中最常用的容器类之一,也是面试的常客.它其实就是前文「数据结构与算法笔记(二)」中「散列表」的实现,处理散列冲突用的是“链表法”,并且在 JDK 1.8 做了优 ...

  5. 【JDK】JDK源码分析-Vector

    概述 上文「JDK源码分析-ArrayList」主要分析了 ArrayList 的实现原理.本文分析 List 接口的另一个实现类:Vector. Vector 的内部实现与 ArrayList 类似 ...

  6. 【JDK】JDK源码分析-ArrayList

    概述 ArrayList 是 List 接口的一个实现类,也是 Java 中最常用的容器实现类之一,可以把它理解为「可变数组」. 我们知道,Java 中的数组初始化时需要指定长度,而且指定后不能改变. ...

  7. 【JDK】JDK源码分析-CountDownLatch

    概述 CountDownLatch 是并发包中的一个工具类,它的典型应用场景为:一个线程等待几个线程执行,待这几个线程结束后,该线程再继续执行. 简单起见,可以把它理解为一个倒数的计数器:初始值为线程 ...

  8. 【JDK】JDK源码分析-HashMap(2)

    前文「JDK源码分析-HashMap(1)」分析了 HashMap 的内部结构和主要方法的实现原理.但是,面试中通常还会问到很多其他的问题,本文简要分析下常见的一些问题. 这里再贴一下 HashMap ...

  9. JDK源码分析(三)—— LinkedList

    参考文档 JDK源码分析(4)之 LinkedList 相关

随机推荐

  1. ural1987 Nested Segments

    Nested Segments Time limit: 1.0 secondMemory limit: 64 MB You are given n segments on a straight lin ...

  2. 搭建git服务器及利用git hook自动布署代码

    注意:服务器:Ubuntu Server 14.04,我的项目比较小,所有操作都使用root,建议最好新建一个用户(需要有管理员权限,否则在后面使用hooks自动部署代码时会出现各种权限问题,很蛋疼的 ...

  3. 第三方app抽奖发送微信红包

    1.控制器方法: private string SendRedPackge(string OpenId, int Amount, string LuckyCode) { Models.PayWeiXi ...

  4. Codeforces AIM Tech Round3

    打得最烂一场Codeforces,多次都错题,无限WA... A题: 题意:给定n个橘子的大小,大小超过b的丢掉,不足d的补充进来,同时超过d的部分去掉,问要去掉几次 分析:直接模拟即可 #inclu ...

  5. UIP源码之ARP过程分析

    之前我们使用UIP实现了tcp和udp通讯今天来说说UIP的实现流程,当然,这篇文章里面只会涉及tcp和udp,暂时还没办法说DHCP,因为UIP的DHCP实现使用了协程的概念,下一章将协程之后再说D ...

  6. 升级apache

    升级Apache到最新版本,本来并不复杂,但是因为涉及到不能停止现有的Apache实例的运行,因此要小心翼翼的做. 大致分成三步: 编译新的Apache, 配置新的Apache, 替换旧的Apache ...

  7. width这样读取出来是一个字符串,并且带有单位,但是offsetwidth返回的是一个数值。

    <!DOCTYPE html> <html> <head> <meta charset=" utf-8"> <title> ...

  8. IOS开发者证书申请及应用上线发布详解(2014版)

    其实一直以来我都想做一个最齐全的上传应用到appstore的教程,但一直狠不下心,今天凌晨2点12分,我鼓起勇气写教程,来吧不多说.登录开发者中心:http://developer.apple.com ...

  9. Masonry布局框架的使用

    Masonry是一个轻量级的布局框架 拥有自己的描述语法 采用更优雅的链式语法封装自动布局 简洁明了 并具有高可读性.比我们使用自动布局,繁琐的约束条件,好用多了.下面我们来学学masonry的使用方 ...

  10. spring DateUtils

    /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreem ...