jdk源码阅读笔记-Integer
public final class Integer extends Number implements Comparable<Integer>
Integer 由final修饰了,所以该类不能够被继承,同时 Integer 继承了Number类,因此可以将Integer转换成 int 、double、float、long、byte和short类型的数据,另外,也实现了comparable接口,因此Integer类也可以进行自然排序。
构造方法只有两个:
public Integer(int value) {
this.value = value;
}
public Integer(String s) throws NumberFormatException {
this.value = parseInt(s, 10);
}
我们主要看第二个构造方法,传入一个字符串,然后调用parseInt方法,接下来进入parseInt的源码:
public static int parseInt(String s, int radix)
throws NumberFormatException
{
/*
* 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 (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 = ;
// //是否为负数
boolean negative = false;
int i = , len = s.length();
//这里加个负号是防止数据溢出,int的数值范围 -2的31次方到2的31次方减一
int limit = -Integer.MAX_VALUE;
//最小基数
int multmin;
//十进制数字
int digit; if (len > ) {
char firstChar = s.charAt();
//第一个字符小于0,有可能是"-","+"或其他字符
if (firstChar < '') { // Possible leading "+" or "-"
//为负数
if (firstChar == '-') {
negative = true;
limit = Integer.MIN_VALUE;
} else if (firstChar != '+')//非数字
throw NumberFormatException.forInputString(s); if (len == ) // Cannot have lone "+" or "-"
throw NumberFormatException.forInputString(s);
i++;
}
/**
* 最小基数,主要防止 result *= radix; 这个操作时数据过大
* 导致数据丢失的问题,因为所以带符号32位int类型整数为-2147483648~2147483647
*/
multmin = limit / radix;
while (i < len) {
// Accumulating negatively avoids surprises near MAX_VALUE
//转换十进制,这里获取的是radix进制下相对应的10进制数字,如:
//Character.digit('a',16),则返回 10;
//若输入字符不在进制的范围之内,则返回 -1:
//Character.digit('t',16),返回 -1,Character.digit('a',10),返回 -1
digit = Character.digit(s.charAt(i++),radix);
//返回-1说明字符非法
if (digit < ) {
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;
}
这个方法中最核心的步骤是1、result *= radix; 2、result -= digit; 经过这两个步骤将字符串转换成数值类型。大概流程是这样的:
假如字符串"1234" 转换成int类型,result 的初始值为0,radix默认为10;
首先截取字符串的第一个字符1(这里忽略各种检查),经过第一步计算 result = 0*10 = 0;第二部计算 result = 0 - 1 = -1;
第一遍循环结束后,result 的值 变成了 -1
截取第二个字符 2 ,result = -1 * 10 = -10,result = -10 - 2 = -12;
截取第三个字符 3 ,result = -12 * 10 = -120,result = -120 - 3 = -123;
截取第四个字符 4 ,result = -123 * 10 = -1230 ,result = -1230-4 = -1234;
循环结束,此时result的值为 -1234,完成字符串向整数型的转换,返回是取反即可。
下面我将从一个面试题引出问题,然后通过阅读源码来解决这个问题。
public static void main(String[] args) {
Integer i1 = ;
Integer i2 = ;
Integer i3 = ;
Integer i4 = ;
Integer i5 = Integer.valueOf();
Integer i6 = Integer.valueOf();
Integer i7 = new Integer();
System.out.println("i1 == i2 的结果是:" + (i1 == i2));
System.out.println("i3 == i4 的结果是:" + (i3 == i4));
System.out.println("i5 == i6 的结果是:" + (i5 == i6));
System.out.println("i1 == i5 的结果是:" + (i1 == i5));
System.out.println("i1 == i7 的结果是:" + (i1 == i7));
}
运行结果为:
i1 == i2 的结果是:true
i3 == i4 的结果是:false
i5 == i6 的结果是:true
i1 == i5 的结果是:true
i1 == i7 的结果是:false
我们先来看第一和第二条结果,同样是比较两个相同数值,为什么会有不同的结果呢?接下我将通过源码来解释原因。
首先,我们通过编译获取到class文件的字节码
从图中我们可以看到,在执行 Integer i1 = 100 这条命令的时候,编译器会调用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);
}
这个代码看起来很简单,Integer 中有一个静态内部类 IntegerCache,调用该方法时首先会判断该值是否在缓存的范围内,如果在则直接将缓存中的数值返回,否则返回一个新对象。看到这里我们似乎已经知道了上面的问题的答案了,接下来继续看静态内部类吧
private static class IntegerCache {
static final int low = -;
static final int high;
static final Integer cache[]; static {
// high value may be configured by property
//缓存范围最小(也是默认范围)为 (-128)~ 127,如果配置java.lang.Integer.IntegerCache.high
//high 的值可从配置文件中读取
int h = ;
String integerCacheHighPropValue =
sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
if (integerCacheHighPropValue != null) {
try {
int i = parseInt(integerCacheHighPropValue);
//获取配置文件和127之间的最大值
i = Math.max(i, );
// Maximum array size is Integer.MAX_VALUE
//最大值范围
h = Math.min(i, Integer.MAX_VALUE - (-low) -);
} catch( NumberFormatException nfe) {
// If the property cannot be parsed into an int, ignore it.
}
}
high = h;
//创建缓存数组
cache = new Integer[(high - low) + ];
int j = low;
//将数字缓存起来默认 -128 ~ 127
for(int k = ; k < cache.length; k++)
cache[k] = new Integer(j++); // range [-128, 127] must be interned (JLS7 5.1.7)
assert IntegerCache.high >= ;
} private IntegerCache() {}
}
我们知道内部类只有在所在类实例化时才会被实例化,而且只会实例化一次,缓存操作是在静态代码块中完成,也就是说在类被实例化的时候数据就已经被缓存好了,接下使用的时候可以直接使用缓存的数据。
现在我们回归到上面的问题,结果1中两个数据均为 100,在缓存的范围中,因此i1和i2都指向的是同一个内存地址,因此返回true。结果2中 两个数都是200,超出了缓存的范围,所以直接new 出了两个对象,因此他们的内存地址不一致,返回结果为false;另外,使用valueOf 和 使用 = 操作符赋值时一样的,所以结果3和结果4返回结果为 true,结果5中 是直接使用new关键字创建对象,所以他们的内存地址肯定不一致,结果为false。
那么,现在问题又来了,那我怎么判断两个整数的大小呢?继续看源码
/**
* The value of the {@code Integer}.
*
* @serial
*/
private final int value;
public boolean equals(Object obj) {
if (obj instanceof Integer) {
return value == ((Integer)obj).intValue();
}
return false;
}
public int intValue() {
return value;
}
是的,没错,比较两个数值大小时可以使用equals方法来比较,源码中value的类型为 int型,intValue返回的也是value,因此可以判断两个数的大小。
public static void main(String[] args) {
Integer i1 = ;
Integer i2 = ;
System.out.println("i1 == i2 的结果是:" + i1.equals(i2)); //true
}
补充:equals 与 == 的区别:
equals 比较的是两个数值的大小,== 有两种情况,如果比较的是 基本数据类型,则 == 跟equals一样都是比较的大小,如果是引用类型或数组,则比较是内存地址。
getChars方法:
static void getChars(int i, int index, char[] buf) {
int q, r;
int charPos = index;
char sign = ; if (i < ) {
sign = '-';
i = -i;
} // Generate two digits per iteration
//每次循环获取后两位数
while (i >= ) {
q = i / ;
// really: r = i - (q * 100);
//使用位移运算的效率高于乘法运算,r为后两位数
r = i - ((q << ) + (q << ) + (q << ));
i = q;
//获取后两位数的个位
buf [--charPos] = DigitOnes[r];
//十位
buf [--charPos] = DigitTens[r];
} // Fall thru to fast mode for smaller numbers
// assert(i <= 65536, i);
//每次只取个位数
for (;;) {
//相当于i*(52429/524288)=i*0.10000038146972656=i*0.1=i/10
//这里选 52429 和 2的19次方相除,得到的结果精度更加高,更加接近于 i/10的结果
//之所以要这样转换,是因为在计算机运算中位移的效率 > 乘法效率 > 除法效率
q = (i * ) >>> (+);
r = i - ((q << ) + (q << )); // r = i-(q*10) ...
buf [--charPos] = digits [r];
i = q;
if (i == ) break;
}
if (sign != ) {
buf [--charPos] = sign;
}
}
jdk源码阅读笔记-Integer的更多相关文章
- jdk源码阅读笔记-LinkedHashMap
Map是Java collection framework 中重要的组成部分,特别是HashMap是在我们在日常的开发的过程中使用的最多的一个集合.但是遗憾的是,存放在HashMap中元素都是无序的, ...
- JDK源码学习笔记——Integer
一.类定义 public final class Integer extends Number implements Comparable<Integer> 二.属性 private fi ...
- jdk源码阅读笔记-ArrayList
一.ArrayList概述 首先我们来说一下ArrayList是什么?它解决了什么问题?ArrayList其实是一个数组,但是有区别于一般的数组,它是一个可以动态改变大小的动态数组.ArrayList ...
- jdk源码阅读笔记-HashSet
通过阅读源码发现,HashSet底层的实现源码其实就是调用HashMap的方法实现的,所以如果你阅读过HashMap或对HashMap比较熟悉的话,那么阅读HashSet就很轻松,也很容易理解了.我之 ...
- jdk源码阅读笔记
1.环境搭建 http://www.komorebishao.com/2020/idea-java-jdk-funyard/ 2. 好的源码阅读资源 https://zhuanlan.zhihu.co ...
- jdk源码阅读笔记-HashMap
文章出处:[noblogs-it技术博客网站]的博客:jdk1.8源码分析 在Java语言中使用的最多的数据结构大概右两种,第一种是数组,比如Array,ArrayList,第二种链表,比如Array ...
- jdk源码阅读笔记-LinkedList
一.LinkedList概述 LinkedList的底层数据结构为双向链表结构,与ArrayList相同的是LinkedList也可以存储相同或null的元素.相对于ArrayList来说,Linke ...
- jdk源码阅读笔记-AbstractStringBuilder
AbstractStringBuilder 在java.lang 包中,是一个抽象类,实现 Appendable 接口和 CharSequence 接口,这个类的诞生是为了解决 String 类在创建 ...
- jdk源码阅读笔记-String
本人自学java两年,有幸初入这个行业,所以功力尚浅,本着学习与交流的态度写一些学习随笔,什么错误的地方,热烈地希望园友们提出来,我们共同进步!这是我入园写的第一篇文章,写得可能会很乱. 一.什么是S ...
随机推荐
- ExtJs radiogroup form.loadRecord方法无法赋值正确解决办法
一.radiogroup的name和radio的name一致,inputValue为整形 { xtype: 'radiogroup', fieldLabel: '是否有效', name: 'statu ...
- Zookeeper + Dubbo + SpringMVC + dubbo-admin
第一步:在CentOS/Windows上安装Zookeeper[前提] A:CentOS Zookeeper作为Dubbo服务的注册中心,Dubbo原先基于数据库的注册中心,没采用Zookee ...
- Ubuntu下vim中文乱码
在linux中,用vim打开包含中文的文件时,有可能出现乱码 下面的vim配置方法亲测有效 1. 找到你的vimrc文件,也有可能是.vimrc,我的服务器是vimrc,我改的是 有的说建议不要改全局 ...
- 最详细的JavaWeb开发基础之java环境搭建(Windows版)
阅读文本大概需要 3 分钟. 首先欢迎大家来学习JavaWeb,在这里会给你比较详细的教程,从最基本的开始,循序渐进的深入.会让初学者的你少踩很多坑(大实话),如果你已经掌握了JavaWeb开发的基础 ...
- Java多线程问题
一. Java多线程: Java给多线程编程提供了内置的支持.一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务. 多线程是多任务的一种特别的形式,但多线 ...
- 用 Python 鉴别色色的图片
0 前言 实话实说啊,这个标题起得就有点标题党,识别是识别,准确率就有点玄学了. 1 环境说明 Win10 系统下 Python3,编译器是 Pycharm,需要安装 nonude 这个库. Pych ...
- 最好的营销是“调情”
每一个精彩的营销案例都应该像一个精彩的故事,而故事最精彩的讲述方式就是设置一个开放的结局,把解读和诠释的权利留给读者和观众.宣讲.洗脑式的营销时代已经终结,就像单相思的深情表白永远不如两情相悦的彼此挑 ...
- java中Iterator和ListIterator的区别与联系
首先,为什么需要使用迭代器Iterator,原有的for循环和for each不能满足需求吗? 那是因为Iterator模式是用于遍历集合类的标准访问方法.它可以把访问逻辑从不同类型的集合类中抽象出来 ...
- SpringCloud实战-Zuul网关服务
为什么需要网关呢? 我们知道我们要进入一个服务本身,很明显我们没有特别好的办法,直接输入IP地址+端口号,我们知道这样的做法很糟糕的,这样的做法大有问题,首先暴露了我们实体机器的IP地址,别人一看你的 ...
- Circular view path [home]: would dispatch back to the current handler URL [/home] again. Check your ViewResolver setup!
Circular view path [home]: would dispatch back to the current handler URL [/home] again. Check your ...