目录
- 一、Integer概览
- 二、integer的方法分解
- 2.1、属性
- 2.2、方法
- 2.2.1 构造Integer
- 2.2.2 辅助方法
- 2.2.3 int转String
- 2.2.4 String 转 int
- 2.2.4 String 转 Integer
- 2.2.5 系统属性的Integer对象获取
- 2.2.5 比较方法
- 3、内部类
- 三、参考资料
一、Integer概览
1.1、Integer.class功能概述
- Integer类包装一个对象中的原始类型int的值;
- 类型为Integer的对象包含一个单一字段,其类型为int ;
- 此外,该类还提供了一些将int转换为String和String转换为int ,以及在处理int时有用的其他常量和方法;
1.2、Integer的UML类图
- 实现了Comparable接口,可用于比较,compareTo方法很简单,就是单纯的比较数字大小,可能返回-1,0,1
- Integer 继承了 Number 类,所以该类可以调用 longValue、floatValue、doubleValue等系列方法返回对应的类型的值;
- Number实现了Serializable接口,所以Integer也可以实现序列化。
二、integer的方法分解
2.1、属性
2.1.1 原生属性
2.1.1.1 MIN_VALUE
/**
* A constant holding the minimum value an {@code int} can
* have, -2<sup>31</sup>.
*/
@Native public static final int MIN_VALUE = 0x80000000;
MIN_VALUE:
- 原生公共静态常量,在类加载时被加载。
- Integer的最小值:-2^31。
0x80000000解析:
- 0x代表16进制数字。
- 一个16进制数对应4个2进制数;此处8个16进制数,等同于4*8=32个2进制数。
- 首位16进制数8等同于2进制数1000;2进制数1000首位为1,代表负数。
- 综上:将0x80000000转为2进制补码后就是-2^31。
2.1.1.2 MAX_VALUE
/**
* A constant holding the maximum value an {@code int} can
* have, 2<sup>31</sup>-1.
*/
@Native public static final int MAX_VALUE = 0x7fffffff;
MAX_VALUE:
- 原生公共静态常量,在类加载时被加载。
- 与MIN_VALUE同理,Integer最大数值:2^31-1
2.1.1.3 SIZE
/**
* 用于表示{@code int}值的二进制补码的位数。
* The number of bits used to represent an {@code int} value in two's
* complement binary form.
* @since 1.5
*/
@Native public static final int SIZE = 32;
SIZE:
- Integer的位数,固定为32
2.1.1.4 serialVersionUID
/** use serialVersionUID from JDK 1.0.2 for interoperability */
@Native private static final long serialVersionUID = 1360826667806852920L;
- Integer 序列号,Long型;
- 私有原生静态常量
2.1.2 非原生属性
2.1.2.1 TYPE
/**
* The {@code Class} instance representing the primitive type
* {@code int}.
* @since JDK1.1
*/
@SuppressWarnings("unchecked")
public static final Class<Integer> TYPE = (Class<Integer>) Class.getPrimitiveClass("int");
TYPE:
- 公共静态常量
- int的类对象int.class,即int的Class实例;
- int是基本类型,其类对象在jvm启动时加载;
- 故 TYPE代表了基本类型int的类对象(Class实例):Integer.TYPE == int.class true;
- Integer.class == int.class false
int的类对象与Integer的类对象的区别:
- int是基本类型,其类对象在jvm启动时加载;
- Integer是一个与其他类无分别的平常类,其类对象在运行时加载;
- 参考资料:和大家讨论一下 int.class 与 Integer.class?
2.1.2.1.1 Class.getPrimitiveClass(“int”):
/**
* Return the Virtual Machine's Class object for the named primitive type.
*/
static native Class<?> getPrimitiveClass(String name);
- Class类中一个静态原生方法;
- 返回指定基元类型的虚拟机中的类对象;
- 由形参name指定需要获取的类对象;
- 由Class<?> 中的?来限定返回类对象的类型,Integer中,限定返回的必须为Integer类型的类对象,int的类对象;
2.1.2.2 digits
/**
* All possible chars for representing a number as a String
*/
final static char[] digits = {
'0' , '1' , '2' , '3' , '4' , '5' ,
'6' , '7' , '8' , '9' , 'a' , 'b' ,
'c' , 'd' , 'e' , 'f' , 'g' , 'h' ,
'i' , 'j' , 'k' , 'l' , 'm' , 'n' ,
'o' , 'p' , 'q' , 'r' , 's' , 't' ,
'u' , 'v' , 'w' , 'x' , 'y' , 'z'
};
digits:
- 将数子转为字符串时组成字符串的字符的所有可能;
- 10个0-9的数字 + 26个 a -z的字母:可以表示 0 -36 之间的所有数字;
- 支持从 2进制 到 36进制 之间所有进制类型的转换;
- 数组的索引与该索引元素的值是相等的;
2.1.2.3 DigitTens && DigitOnes
final static char [] DigitTens = {
'0', '0', '0', '0', '0', '0', '0', '0', '0', '0',
'1', '1', '1', '1', '1', '1', '1', '1', '1', '1',
'2', '2', '2', '2', '2', '2', '2', '2', '2', '2',
'3', '3', '3', '3', '3', '3', '3', '3', '3', '3',
'4', '4', '4', '4', '4', '4', '4', '4', '4', '4',
'5', '5', '5', '5', '5', '5', '5', '5', '5', '5',
'6', '6', '6', '6', '6', '6', '6', '6', '6', '6',
'7', '7', '7', '7', '7', '7', '7', '7', '7', '7',
'8', '8', '8', '8', '8', '8', '8', '8', '8', '8',
'9', '9', '9', '9', '9', '9', '9', '9', '9', '9',
} ;
final static char [] DigitOnes = {
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
} ;
- 一个0 - 99 之间的整数(xy),可以通过以该整数为下标从数组取数的方式获取其个位的数值和十位的数值;以DigitOnes [xy] 获取xy的个位数字y,以DigitTens [xy]获取xy的十位数字x。
- 例1:69这个整数, DigitOnes[69] = 9; DigitTens [69] = 6;
- 例2:69这个整数, DigitOnes[47] = 7; DigitTens [69] = 4;
- 例3:5这个整数, DigitOnes[5] = 5; DigitTens [5] = 0;(5 即 05 )
- 例4:30这个整数, DigitOnes[30] = 0; DigitTens [5] = 3;
- 分析:
- 数组下标都是从0开始计数的;0-99之间的数字大小都在0-9范围内(不论十位数字还是个位数字,都是从0开始,到9结束)。
- DigitOnes 数组,其索引中的个位的数字刚好就是这个索引的值;
- DigitTens 数组,其索引中十位的数字刚好就是这个索引的值;
- 综上,任何情况下,在0-99范围内的数字,都可以通过将该数字作为索引从DigitOnes 和 DigitTens数组获取 对应的个位的数值和对应的十位的数值;
参考资料:算法学习之测试篇
2.1.2.4 sizeTable
final static int [] sizeTable = { 9, 99, 999, 9999, 99999, 999999, 9999999,99999999, 999999999, Integer.MAX_VALUE };
sizeTable:
- 静态int数组常量,其元素是十进制数字中每个位数(1位数,2位数,3位数…10位数:组成数值的数字个数)中最大值;
- 数组中元素所在索引 + 1 刚好是该元素的数字个数(位数);
- 可与方法stringSize(int x)组合使用,获取x的字符数量;
2.1.2.5 value
/**
* The value of the {@code Integer}.
*
* @serial
*/
private final int value;
- 私有常量
- 存放Integer对象具体内容(数值);
2.2、方法
2.2.1 构造Integer
/**
* Constructs a newly allocated {@code Integer} object that
* represents the specified {@code int} value.
*
* @param value the value to be represented by the
* {@code Integer} object.
*/
public Integer(int value) {
this.value = value;
}
/**
* Constructs a newly allocated {@code Integer} object that
* represents the {@code int} value indicated by the
* {@code String} parameter. The string is converted to an
* {@code int} value in exactly the manner used by the
* {@code parseInt} method for radix 10.
*
* @param s the {@code String} to be converted to an
* {@code Integer}.
* @exception NumberFormatException if the {@code String} does not
* contain a parsable integer.
* @see java.lang.Integer#parseInt(java.lang.String, int)
*/
public Integer(String s) throws NumberFormatException {
this.value = parseInt(s, 10);
}
分析:
- 提供2种构造方法,分别根据传入的int类型参数和String类型参数构造Integer;
- 构造Integer是都是通过属性value存放数值;
通过String构造Integer时:
- String不能为空,默认构造10进制数据;
- 通过parseInt方法将String转为10进制Integer;
2.2.2 辅助方法
2.2.2.1 stringSize(int x)
// Requires positive x :x必须为正整数
static int stringSize(int x) {
for (int i=0; ; i++)
//轮询在sizeTable数组中找到比x大的最小元素,这个元素的索引+1即是x的字符数量(String长度)
if (x <= sizeTable[i])
return i+1;
}
- 获取int类型X的数组个数(字符串个数)
- 极限数值对比
2.2.2.2 getChars(int i, int index, char[] buf)
/**
* 将i中表示整数的字符从index索引处开始倒序放入buf[]中
* Will fail if i == Integer.MIN_VALUE
*/
static void getChars(int i, int index, char[] buf) {
int q, r;
int charPos = index;
char sign = 0;
//针对负数,设置负数符号,将负数强转为正整数
if (i < 0) {
sign = '-';
i = -i;
}
// Generate two digits per iteration
//当i > 65536时,轮询获取i的后两位数字r放入buf中
while (i >= 65536) {
q = i / 100;//取商
// really: r = i - (q * 100);
r = i - ((q << 6) + (q << 5) + (q << 2));//等价于:r = i % 100,即r等于i除以100的余数(00 <= r < 100)
i = q;//将i/100的值赋值给i作为下一次轮询的i值:i值每次轮询少后面2位,数值一直在减小
//将i % 100的值r中的个位数与十位数分别取出放入buf中,十位数放在个位数的前一个索引位置;索引递减
//思路详情:请见本页面[1.2.3 DigitTens && DigitOnes]分析
buf [--charPos] = DigitOnes[r];//取r中个位数:取数字r%10的结果
buf [--charPos] = DigitTens[r];//取r中的十位数:取数字r/10的结果
}
// Fall thru to fast mode for smaller numbers
// assert(i <= 65536, i); 当 i < = 65536时
for (;;) {
q = (i * 52429) >>> (16+3);//等价于: q = i * 0.1取整 or q = i / 10 取商
r = i - ((q << 3) + (q << 1)); // r = i-(q*10) 等价于:r = i % 10 取余(0 <= r < 10)
//将i中的个位数字字符放入buf中
//思路详情:请见本页面[1.2.2 digits]分析
buf [--charPos] = digits [r];
i = q;
if (i == 0) break;
}
//负数符号拼接
if (sign != 0) {
buf [--charPos] = sign;
}
}
- 代码理解
- q = i / 100 与 r = i - ((q << 6) + (q << 5) + (q << 2));
- (q << 6) + (q << 5) + (q << 2) == q * 2 ^6 + q *
2^5 + q2^2 == q * (64 + 32 + 4) == q100; - 故上面等式可转换为:r = i - q * 100;
- 结合 q = i / 100,则等式可转为 r = i % 100;
- (q << 6) + (q << 5) + (q << 2) == q * 2 ^6 + q *
- q = (i * 52429) >>> (16+3) 与 r = i - ((q << 3) + (q << 1));
- q = (i * 52429) >>> (16+3) == q = (i * 52429) / 2^19 == q = (i * 52429) /52488 == q = i * 0.1;
- 即q = (i * 52429) >>> (16+3) 等价于 q = i / 10(取商)
- r = i - ((q << 3) + (q << 1)) == r = i - q * 10;结合q = i / 10(取商),则等价于 r = i %10(取余);
- q = i / 100 与 r = i - ((q << 6) + (q << 5) + (q << 2));
- 用位运算而不直接全用出发取商或取余的原因:
- 移位的效率比直接乘除的效率要高;
- 乘法的效率比除法的效率要高;
- 以65536作为分解值的原有分析:
参考资料:Java 基础:Integer 源码分析
2.2.3 int转String
public static String toString(int i, int radix);//按照2-36进制之间的进制转int为String
public static String toUnsignedString(int i, int radix);//按照2-36进制之间的进制无符号数的转int为String
public static String toHexString(int i); // 16进制字符串表示
public static String toOctalString(int i); // 8进制字符串表示
public static String toBinaryString(int i); // 2进制字符串表示
public static String toString(int i); // 十进制字符串表示
public static String toUnsignedString(int i); // 10进制无符号数的字符串,调用第二个方法,radix=10
public String toString();//默认转为10进制数据
2.2.3.1 toString(int i, int radix)
/**
* 将int数据按照radix进制转换成String
* @param i 被转出String的10进制int数字.
* @param radix 按照什么进制转换成String:2-36进制之间
* @return 一个符合进制要求的String
* @see java.lang.Character#MAX_RADIX:36
* @see java.lang.Character#MIN_RADIX:2
**/
public static String toString(int i, int radix) {
//进制需要再2-36之间,否则强制转换Wie10进制
if (radix < Character.MIN_RADIX || radix > Character.MAX_RADIX)
radix = 10;
//10进制快速转换调用方法toString(i)
/* Use the faster version */
if (radix == 10) {
return toString(i);
}
// 定义长度为33的char数组,第一个位置放数字的符号(+、-)
char buf[] = new char[33];
boolean negative = (i < 0);//正负判断
int charPos = 32;
if (!negative) {
i = -i;//负数强转为正数,方便计算
}
//进制转换(有10进制转为其他进制,通过取余方式转换)
while (i <= -radix) {//等价于:i >= radix,个人猜测源码这样写性能更强
buf[charPos--] = digits[-(i % radix)];
i = i / radix;
}
buf[charPos] = digits[-i];
//负数符号添加
if (negative) {
buf[--charPos] = '-';
}
//字符数组实例化一个String实例返回
return new String(buf, charPos, (33 - charPos));
}
- 整体转换思路如下:
- 进制确保在2 - 36之间,radix在2-36范围外,设置默认进制10;
- 针对10进制转换,直接调用toString(i)实现快速转换;
- 针对非10进制转换,轮询通过 i % radix取余将余数在字符数组中从后向前放;然后添加正负符号;
- 最后根据字符数组实例化一个String实例返回;
- 将int转为字符数组,在用字符数组实例化String,是因为String中通过char[]存放字符串中的字符;
2.2.3.2 toString(int i)
/**
* 返回i的10进制字符串
* @param i 被转为字符串的int参数
* @return i的10进制字符串
*/
public static String toString(int i) {
//当i为最小Integer时,直接返回字符串"-2147483648"
if (i == Integer.MIN_VALUE)
return "-2147483648";
//获取i的字符个数(具体思路请看本页面的stringSize(int x)分析)
int size = (i < 0) ? stringSize(-i) + 1 : stringSize(i);
//构建字符数组存放i的字符
char[] buf = new char[size];
//获取i的字符数组
//详细处理逻辑:请见本业[2.2.2 getChars(int i, int index, char[] buf)]
getChars(i, size, buf);
//根据i的字符数组构建String的实例化对象
return new String(buf, true);
}
2.2.3.2 toHexString(int i)、toOctalString(int i)、toBinaryString(int i)
//十进制转十六进制并返回字符串
public static String toHexString(int i) {
return toUnsignedString0(i, 4);
}
//十进制转8进制并返回字符串
public static String toOctalString(int i) {
return toUnsignedString0(i, 3);
}
//十进制转2进制并返回字符串
public static String toBinaryString(int i) {
return toUnsignedString0(i, 1);
}
- 分析:
- 1-二进制, 3-八进制(三位二进制数表示一位八进制数), 4-十六进制(四位二进制数表示一位十六进制数);
- 三个方法都有调用toUnsignedString0(int val, int shift)方法;
- 参考资料:jdk源码:Integer.toUnsignedString0
2.2.3.2.1 toUnsignedString0(int val, int shift)
/**
* 根据shift将int数据转为对应进制的String实例
* Convert the integer to an unsigned number.
*/
private static String toUnsignedString0(int val, int shift) {
// assert shift > 0 && shift <=5 : "Illegal shift value";
//获取val在内存中实际占位数量
//32减去numberOfLeadingZeros, 表示实际表示此数字只需要的位数。
//比如10的二进制补码是0000 0000 0000 0000 0000 0000 0000 1010,
//它实际只需要1010这4位数字就可以代表10. 所以10的mag就是4
int mag = Integer.SIZE - Integer.numberOfLeadingZeros(val);//详情见本目录[2.3.2.3 numberOfLeadingZeros(int i) ]
//计算var实际需要的字符数组长度
//如果要转成二进制shift=1,则chars=4,因为需要长度为4的字符数组来存放1010。
//如果要转成16进制a,则shift=4,得出chars=1,因为只需要长度为1的字符串数组来存放结果a.
int chars = Math.max(((mag + (shift - 1)) / shift), 1);
char[] buf = new char[chars];
//将var进行进制转换并将转换后的数字字符存放在字符数组中
formatUnsignedInt(val, shift, buf, 0, chars);
// Use special constructor which takes over "buf".
//根据buf实例化String对象
//在String(char[] value, boolean share)中,share必须为true,否则不支持
return new String(buf, true);
}
参考资料:jdk源码:Integer.toUnsignedString0
2.2.3.2.2 formatUnsignedInt(int val, int shift, char[] buf, int offset, int len)
/**
* 将long(视为无符号)格式化为字符数组
* @param val 将要格式化的无符号数字参数
* @param shift 格式化的基数:1-二进制,3-八进制,4-十六进制
* @param buf 格式化后存放字符的字符数组
* @param offset 目标数组中开始位置的偏移量
* @param len 要写入的字符数
* @return 使用的最低字符位置
*/
static int formatUnsignedInt(int val, int shift, char[] buf, int offset, int len) {
int charPos = len;
//根据格式化基数计算需要转化的进制:1*2^shift
int radix = 1 << shift;
int mask = radix - 1;//计算 掩码:比radix小1
do {
//偏移量 + 共计需要写入的字符数 == 最开始第一个字符在buf数组中的位置
//digts数组详情:请见本页[1.2.2 digits]
buf[offset + --charPos] = Integer.digits[val & mask];//等价于 var % radix
val >>>= shift;//等价于:var = var / 2^shift;
//循环条件:还存在没有放入buf的var和buf中还有位置
} while (val != 0 && charPos > 0);
//返回var的字符在buf中的最低位置
return charPos;
}
2.2.3.2.3 numberOfLeadingZeros(int i)
/**
* 计算int数据i转为二进制后前面有多少个0:此方法有一个缺陷,详情请见本目录下的参考资料
* 表示该数字以二进制补码形式表示时最高位 1 前面的位数。
* 比如 16L,二进制为 0001 0000, 最高位 1 在第 4 位,则前面有 59 个 0,表示 16L 实际需要的位数是 Long.SIZE - 59 = 5,即 10000。
* 对于负数来说,符号位为 1,所以永远需要 64 位来表示。
*
*/
public static int numberOfLeadingZeros(int i) {
// HD, Figure 5-6
if (i == 0)
return 32;
int n = 1;//符号位
if (i >>> 16 == 0) { n += 16; i <<= 16; }//i右移16位 左移赋值:使得i左边没有0
if (i >>> 24 == 0) { n += 8; i <<= 8; }//i右移24位 左移赋值
if (i >>> 28 == 0) { n += 4; i <<= 4; }//i右移28位 左移赋值
if (i >>> 30 == 0) { n += 2; i <<= 2; }//i右移30位:表明i的左边有表格非1字符
n -= i >>> 31;//i>>>31后等于2:由于前面i左移后i的左边没有0,然后在右移31位,则i的左边有31个0,等价于 i >>> 31 == 0001 == 2^1 == 2;
return n;
}
参考资料:
2.2.3.3 toUnsignedLong(int x)
/**
* 给定参数int转换为无符号的long
* 无符号转换为long时,高32位为扩充为0,也就是零位扩展;低32位等同参数int
* 因此,0和int正数 与对应的long的值相同,负数等于参数int+2^32
*/
public static long toUnsignedLong(int x) {
return ((long) x) & 0xffffffffL;
}
2.2.3.4 toUnsignedString(int i, int radix)、toUnsignedString(int i)
public static String toUnsignedString(int i, int radix) {
return Long.toUnsignedString(toUnsignedLong(i), radix);
}
public static String toUnsignedString(int i) {
return Long.toString(toUnsignedLong(i));
}
解析:
- 都是先调用toUnsignedLong(int x)将int转为long,然后调用Long中方法将long转为String
2.2.4 String 转 int
public static int parseInt(String s, int radix) throws NumberFormatException;//将radix进制String转为10进制int
public static int parseInt(String s) throws NumberFormatException;//将10进制String转为10进制int
public static int parseUnsignedInt(String s, int radix) throws NumberFormatException;//将radix进制无符号String转为10进制的int数
public static int parseUnsignedInt(String s) throws NumberFormatException;//将10进制无符号String转为10进制的int数
2.2.4.1 parseInt(String s, int radix)
/**
* 将radix进制的String数据转为10进制的int数据返回
* 警告:在初始化IntegerCache之前,可能会在VM初始化的早期调用此方法。必须注意不要使用valueOf方法。
*
* 注意:String 中每一个字符都必须比进制基数radix小且大于0
*/
public static int parseInt(String s, int radix)
throws NumberFormatException
{
//待转换的String数据不能为null;2 <= radix <= 32;
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) {
//获取String数据s的首个字符,用作判断String数据的正负
char firstChar = s.charAt(0);
if (firstChar < '0') { Character.hashCode('0')//等于48,'-'和'+'均小于它
//如果第一个字符是正负符号,则非负既正
if (firstChar == '-') {
negative = true;//负数标志
limit = Integer.MIN_VALUE;//最小值为-2^31
} else if (firstChar != '+')
throw NumberFormatException.forInputString(s);
//String数据中不能只有正负符号
if (len == 1) // Cannot have lone "+" or "-"
throw NumberFormatException.forInputString(s);
i++;
}
multmin = limit / radix;
while (i < len) {//i作为String数据中首个数字所在索引;
// Accumulating negatively avoids surprises near MAX_VALUE
//每一位提取出来对应进制的数字
//i初始值代表从左向右第一个数字的索引
//在循环提前String的每一个字符过程中,i递增,则代表字符是从左向右提取的,即对于String所代表的数字来说,是从高位向地位提取的(1888:先提取千位所在的1,再提取百位所在的8,...异常向右/地位提取)
digit = Character.digit(s.charAt(i++),radix);
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;//result从0开始累计减小
}
} else {
throw NumberFormatException.forInputString(s);
}
return negative ? result : -result;
}
将radix进制的String数据转为10进制的int数据返回
注意事项:
- String 中每一个字符都必须比进制基数radix小且大于0,否则报异常;
- 进制基数radix必须在范围2 - 36之间;
- String除了首个字符可以为 “+”、“-”符号外,不可以为非数字;小数点除外;
- 转换后的10进制int数值范围必须在 -2^31 ~ 2^31-1之间
核心代码分析:
- digit = Character.digit(s.charAt(i++),radix); result *= radix; result -= digit;
- 在循环提前String的每一个字符过程中,i递增,则代表字符是从左向右提取的,即对于String所代表的数字来说,是从高位向地位提取的(1888:先提取千位所在的1,再提取百位所在的8,…依次向右/地位提取);
- reult从0开始,在第一次执行完核心代码后被转为负数;
- digit = Character.digit(s.charAt(i++),radix); result *= radix; result -= digit 等价于:每次循环获取的digit * radix^(len - i -1)的值相加;
- 例:String s = 875,radix = 9;—>len = 3;
- 第一次循环时:i = 0,digit=8,result = 0 * 9 = 0;result = 0-8 = -8;
- 第二次循环时:i = 1,digit=7,result = -8 * 9 ;result = -8 * 9 - 7 ;
- 第三次循环时:i = 2,digit=5,result = (-8 * 9 - 7)9 = -89^2 - 7*9^1,result = -8 * 9^2 - 7 * 9^1 - 5;
- 综上:result = (-8 * 9^(3-0-1)) + (- 7 * 9^(3-1-1) ) + ( - 5*9^(3-2-1))
parseInt(String s)默认String为10进制数据,即将10进制String转为10进制int;调用parseInt(String s, int radix),radix = 10;
2.2.4.2 parseUnsignedInt(String s, int radix)
public static int parseUnsignedInt(String s, int radix)
throws NumberFormatException {
//String参数不能为null
if (s == null) {
throw new NumberFormatException("null");
}
int len = s.length();
if (len > 0) {//字符串长度必须大于0
char firstChar = s.charAt(0);
if (firstChar == '-') {//首字符不能为“-”
throw new
NumberFormatException(String.format("Illegal leading minus sign " +
"on unsigned string %s.", s));
} else {
//此处筛选小于等于Integer.MAX_VALUE(2^31-1)的String数据
if (len <= 5 || // Integer.MAX_VALUE in Character.MAX_RADIX is 6 digits:Integer.MAX_VALUE用最大进制32进制表示时需要6个digits字符
(radix == 10 && len <= 9) ) { // Integer.MAX_VALUE in base 10 is 10 digits:Integer.MAX_VALUE用10进制表示时需要10个digit字符
return parseInt(s, radix);
} else {//此处处理大于Integer.MAX_VALUE的String
long ell = Long.parseLong(s, radix);
//无符号int数据的最大值限制:2^32-1
if ((ell & 0xffff_ffff_0000_0000L) == 0) {
return (int) ell;
} else {
throw new
NumberFormatException(String.format("String value %s exceeds " +
"range of unsigned int.", s));
}
}
}
} else {
throw NumberFormatException.forInputString(s);
}
}
String表示的最大无符号数字是2^32 - 1;
- 有符号int数值大小范围:-2^31 ~ 2^31 - 1;
- 无符号int数值范围范围:0 ~ 2^32- 1;
整体思路:
- radix进制的String数据用10进制表示 <= 2^31-1时,用parseInt(String s, int radix)转换 > 2^31-1时用Long.parseLong(s, radix)转换;
- 在String转为10进制后的值大于2^32-1时,报错数据越界;
注意事项:
- String参数不能为null或长度不能小于等于0;
- String参数首字母除了可以为“+”,只能为数字;
- radix大小范围:2 - 36;
- String的每个字符大小范围:0 <= 字符 < radix;
- String的数字范围:0 ~ 2^32- ;
parseUnsignedInt(String s)默认调用parseUnsignedInt(String s, int radix),radix=10;
参考资料:Java基本数据类型自动转型溢出问题
2.2.4 String 转 Integer
public static Integer valueOf(String s, int radix);//将radix进制String转为10进制Integer
public static Integer valueOf(String s) throws NumberFormatException;//将10进制String转为10进制Integer
public static Integer valueOf(int i);//获取int的Integer对象
public static Integer decode(String nm) throws NumberFormatException;//将8/16/10进制的String nm转为10进制Integer对象(String的进制根据String的开始字符确定)
2.2.4.1 valueOf(int i)
/**
* 快速返回int值的Integer对象
*
* @param i an {@code int} value.
* @return an {@code Integer} instance representing {@code i}.
* @since 1.5
*/
public static Integer valueOf(int i) {
//数字i在缓存范围(-128~127)内,直接返回缓存中保存的i值对应的Iteger对象,不再new新的Integer对象,相对于直接new,性能更快更好;
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
//非缓存范围内的i值,new一个新的Integer对象
return new Integer(i);
}
对于需要获取int数据的Integer对象的需求,如果int数值在Integer缓存范围内,调用此方法性能更强;
Integer缓存默认范围:-128~127;其上限可以通过配置调整;
valueOf(String s, int radix)先调用parseInt(String s, int radix)获取String的10进制int,在调用valueOf(int i)获取10进制int对应Integer对象
valueOf(String s)与valueOf(String s, int radix)类型,只是radix指定为10;
Integer缓存的优势:详情请见本页面 3.1 IntegerCache
2.2.4.1 decode(String nm)
//将8/16/10进制的String nm转为10进制Integer对象(String的进制根据String的开始字符确定)
public static Integer decode(String nm) throws NumberFormatException {
int radix = 10;
int index = 0;
boolean negative = false;
Integer result;
if (nm.length() == 0)
throw new NumberFormatException("Zero length string");
//正负判断
char firstChar = nm.charAt(0);
// Handle sign, if present
if (firstChar == '-') {
negative = true;
index++;
} else if (firstChar == '+')
index++;
// Handle radix specifier, if present:进制判断
if (nm.startsWith("0x", index) || nm.startsWith("0X", index)) {
index += 2;
radix = 16;
}
else if (nm.startsWith("#", index)) {
index ++;
radix = 16;
}
else if (nm.startsWith("0", index) && nm.length() > 1 + index) {
index ++;
radix = 8;
}
if (nm.startsWith("-", index) || nm.startsWith("+", index))
throw new NumberFormatException("Sign character in wrong position");
try {
//将radix进制的nm转为对应的Integer对象;
//此时如果nm无符号10进制的数值等于整数的最小值的绝对值,会报错NumberFormatException ,;
//因为int的最小值(-2^31)的绝对值比int的最大值(2^31-1)大1;
//nm.substring(index)等价于Sring nm的无符号数值
result = Integer.valueOf(nm.substring(index), radix);
result = negative ? Integer.valueOf(-result.intValue()) : result;//正负确定:负数转正在获取Iteger对象
} catch (NumberFormatException e) {
// If number is Integer.MIN_VALUE, we'll end up here. The next line
// handles this case, and causes any genuine format error to be
// rethrown.
//结果为整数的最小值时,走这里
String constant = negative ? ("-" + nm.substring(index))
: nm.substring(index);
result = Integer.valueOf(constant, radix);
}
return result;
}
注意事项:
- 字符串nm的长度不能为0;
- 正(+)负(-)符合只能出现在首字符;
- 默认进制为10进制;
思路解析:
- 通过首字符判断String nm的正负;
- 通过非正负符号的开始字符判断进制:0x/0X/#–16进制,0–8进制;
- 先将radix进制的无符号String nm转为10进制的无符号Integer(0-2^32-1);
- 此时如果nm.substring(index)的值如果是整数最小值的绝对值时,会抛异常NumberFormatException ;
- 如果nm.substring(index)小于整数最小值的绝对值,调用valueOf(int i);
- 如果nm.substring(index)等于整数最小值的绝对值,调用valueOf(String s, int radix);
- 对于负数,将无符号Integer转为对应的负Integer;
2.2.5 系统属性的Integer对象获取
public static Integer getInteger(String nm);
public static Integer getInteger(String nm, int val);
public static Integer getInteger(String nm, Integer val);
2.2.5.1 getInteger(String nm, Integer val)
//获取String参数nm代表的系统属性名的Integer对象,val为默认值
public static Integer getInteger(String nm, Integer val) {
String v = null;
try {
v = System.getProperty(nm);//获取系统属性值
} catch (IllegalArgumentException | NullPointerException e) {
}
if (v != null) {
try {
//将系统属性值转为Integer对象
return Integer.decode(v);
} catch (NumberFormatException e) {
}
}
return val;
}
参数说明:
- String nm为系统属性名
- Iteger val为默认值
注意事项:
- 系统属性名nm不能为null,为null报NullPointerException;如果nm符合isEmpty,则报IllegalArgumentException;
- 如果系统属性值为null,则返回默认对象val;
思路解析:
- 调用System.getProperty(nm)获取系统中属性名nm对应的value;
- 调用Integer.decode(v)将获取的String型对应进制的value转为10进制的Integer对象;
说明:
- 系统属性通过System.setProperty(String key, String value)可设置
2.2.5.2 getInteger(String nm, int val)
public static Integer getInteger(String nm, int val) {
Integer result = getInteger(nm, null);
return (result == null) ? Integer.valueOf(val) : result;
}
public static Integer getInteger(String nm) {
return getInteger(nm, null);
}
如代码段所示:
- getInteger(String nm)时:直接调用的getInteger(String nm, Integer val),默认值为null;
- getInteger(String nm, int val)时:
- 先调用getInteger(String nm, Integer val),默认值为空;
- 当getInteger(String nm, Integer val)返回值为null时,调用Integer.valueOf(int i)获取默认int val的Integer对象;
2.2.5 比较方法
public int hashCode()
public static int hashCode(int value)
public boolean equals(Object obj)
public int compareTo(Integer anotherInteger)
public static int compare(int x, int y)
public static int compareUnsigned(int x, int y)
2.2.5.1 hashCode(int value)
public static int hashCode(int value) {
return value;
}
public int hashCode() {
return Integer.hashCode(value);
}
如代码的所示:
- Integer对象的hash码值就是其value值本身
2.2.5.2 equals(Object obj)
public boolean equals(Object obj) {
if (obj instanceof Integer) {
return value == ((Integer)obj).intValue();
}
return false;
}
如代码所示:
- Integer的equls方法要求参数属于Integer对象;
- 对比的本质是hash码值的对比;直接判断hash值是否==
2.2.5.3 compare(int x, int y)
public static int compare(int x, int y) {
return (x < y) ? -1 : ((x == y) ? 0 : 1);
}
Integer的比较,直接比较2个形参的大小;
- 根据比较结果直接返回-1/0/1;
- compareTo(Integer anotherInteger)是通过调用compare(int x, int y)比较Integer对象的hash值;
- compareUnsigned(int x, int y)先将参数转为有符号的int值,再调用compareUnsigned(int x, int y)比较大小;
3、内部类
3.1 IntegerCache
/**
* Cache to support the object identity semantics of autoboxing for values between
* -128 and 127 (inclusive) as required by JLS.
*
* The cache is initialized on first usage. The size of the cache
* may be controlled by the {@code -XX:AutoBoxCacheMax=<size>} option.
* During VM initialization, java.lang.Integer.IntegerCache.high property
* may be set and saved in the private system properties in the
* sun.misc.VM class.
*/
private static class IntegerCache {
static final int low = -128;//最小值
static final int high;//最大值
static final Integer cache[];//Integer数组存放符合要求是Intger对象
//静态块,类加载时被加载
static {
// 缓存最大值可以通过属性进行配置
//high value may be configured by property
int h = 127;
String integerCacheHighPropValue =
sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
//获取缓存当前情况的最大值(其在极端情况下可以获取的最大值为Integer.MAX_VALUE:2^31-1)
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;
//构造缓存数组:数组大小(high - low) + 1,+1的原因是high和low中间还有个分界点数值0
cache = new Integer[(high - low) + 1];
//将low -high之间的Integer对象都存放到缓存数组中
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() {}
}
缓存类的释疑:
- 缓存以支持自动装箱的对象标识语义。
- 默认存放数据大小在-128到127(含)之间。
- 缓存在第一次使用时初始化。缓存的大小可以由{@code-XX:AutoBoxCacheMax=}选项控制(可以通过参数在配置文件中进行配置)。
- 在VM初始化期间,缓存的最大值可以通过sun.misc.VM class的配置文件进行设置。(String integerCacheHighPropValue =
sun.misc.VM.getSavedProperty(“java.lang.Integer.IntegerCache.high”);) - 缓存类中的缓存数组中的元素由静态块加载;
缓存池存在的意义:
- 缓存池中存放常用的Integer对象,在类加载的时候就被加载到内存中(会占用一定的内存空间);
- 针对常用对象,如果该对象在缓存池中存在,在构造Integer是有助于提高性能;
- 如过使用的 Integer 的值在缓存范围的话,就用 Integer i = value 的形式构建对象,如果你的值不在缓存范围内,则使用 Integer i = new Integer(value) 的形式构建 Integer 对象,避免自动装箱的过程
应用场景:
- valueOf(int i)
三、参考资料
1、和大家讨论一下 int.class 与 Integer.class?
2、java基础—Integer源码
3、从 Int 到 Integer 对象,细细品来还是有不少东西
4、Java 基础:Integer 源码分析
5、Integer