Integer源码解析
简介
Integer类主要的作⽤就是对基本类型int进⾏封装,提供了⼀些处理int类型的⽅法,⽐如int到String类型的转换⽅法或String类型到int 类型的转换⽅法,当然也包含与其他类型之间的转换⽅法。除此之外还有⼀些位相关的操作 。
说明:该源码来⾃于 jdk_1.8.0_162 版本。
结构及常⽤⽅法
IntegerCache 内部类
说明:IntegerCache 是 Integer 的⼀个内部类,默认缓存的范围是 [-128,127],当Integer的值范围在 [-128,127] 时则直接从缓存中获取对应的Integer对象,不必重新实例化。这些缓存值都是静态且 final 的,避免重复的实例化和回收。另外我们可以改变这些值缓存的范围,在启动JVM时通过:-Djava.lang.Integer.IntegerCache.high=xxx 就可以改变缓存值的最⼤值,⽐
如:-Djava.lang.Integer.IntegerCache.high=500 则会缓存 [-128,500] 。
注意:新建 Integer对象时尽量使⽤ Integer.valueOf ⽅法以优化性能。
我们通过两道⾯试题来了解⼀下这个缓存
第⼀题:
public static void main(String[] args) {
Integer a = 1;
Integer b = 2;
System.out.println("交换前:" + "a = " + a + " " + "b = " + b);
swap(a, b);
System.out.println("交换后:" + "a = " + a + " " + "b = " + b);
}
private static void swap(Integer numA, Integer numB) {
Integer temp = numA;
numA = numB;
numB = temp;
}
sam怎么读输出结果:
交换前:a = 1 b = 2
交换后:a = 1 b = 2
注意:关于值传递和引⽤传递的问题 。这⾥要特殊考虑String,以及Integer、Double等基本类型包装类,它们的类前⾯都有final修饰,为不可变的类对象,每次操作(new或修改值)都是新⽣成⼀个对象,对形参的修改时,实参不受影响,与值传递的效果类似,但实际上仍是引⽤传递。
说明:Integer a = 1 实际上等价于 Integer a = Integer.valueOf(1) ,这就是⾃动装箱(后⾯会详解) 。尽管 a 和 b 都是 Integer 类型的对象,但是由于 Integer 这个类本⾝就跟 String ⼀样,是⽤ final 修饰的,
并且 value 属性也是 final 修饰的,所以⽆论是调⽤⽅法重新赋值或者直接在 main ⽅法重新赋值,都会产⽣⼀个新的对象,原来的那个对象不会做任何改变,表⾯上都是同⼀个变量名,实际上却是⼀个新的对象 。
我们来看⼀个伪代码:
Integer a = Integer.valueOf(5);
a = Integer.valueOf(10);
等价于
Integer a = Integer.valueOf(5);
Integer newA = Integer.valueOf(10);
a = newA;
垃圾袋英文
结论:所以常规的⽅法不能对他们原始的对象做任何的改变,在调⽤ swap() ⽅法时,实际上 numA 和 numB 的改变跟 a 和 b 并没有任何关系,因为 numA 和 numB ⼀旦修改,所谓改变后的对象那都
高中宾语从句讲解
是新建的对象,所以⽆论 numA 和 numB 如何改变,都不会影响 a 和 b 的值 。那么如果我们想改变 a 和 b 的值怎么办呢,那就是绕过这些 final 和 private 的限制,这⾥我们想到了反射,接着看第⼆题 。
第⼆题:
public static void main(String[] args) throws Exception {
Integer a = 1;
Integer b = 2;
System.out.println("交换前:" + "a = " + a + " " + "b = " + b);
swap(a, b);
System.out.println("交换后:" + "a = " + a + " " + "b = " + b);
Integer c = 1;
System.out.println("c = " + c);
}
private static void swap(Integer numA, Integer numB) throws Exception {
Field field = DeclaredField("value");
field.tAccessible(true);
int temp = numA;
field.t(numA, numB);
field.t(numB, temp);
}
输出结果:
交换前:a = 1 b = 2
交换后:a = 2 b = 2
c = 2
豆腐渣工程的意思
说明:这⼀次我们使⽤反射来尝试替换 a 和 b 的值,但是看到运⾏结果,可能会疑惑:
(1) 为什么交换后 a = 2 b =2 ?
(2) 为什么 c = 2 ?
要解开这些疑问,我们得先把 Integer 的拆箱装箱的问题解决,拆箱装箱是Java语⾔的语法糖,为了看到最终运⾏时的实际代码,最简单的⽅式就是直接反编译看编译过后的代码,我们⽤ jad ⼯具反编译上述的代码得到如下内容:
public static void main(String args[]) throws Exception {
Integer a = Integer.valueOf(1); //⾃动装箱
asons in the sun中文歌词
Integer b = Integer.valueOf(2); //⾃动装箱
初中辅导班招生简章System.out.println((new StringBuilder()).append("a = ").append(a).append("b = ").append(b).toString());
swap(a, b);
System.out.println((new StringBuilder()).append("a = ").append(a).append("b = ").append(b).toString());
Integer c = Integer.valueOf(1);
System.out.println((new StringBuilder()).append("c = ").append(c).toString());
}
private static void swap(Integer numA, Integer numB) throws Exception {
Field field = java/DeclaredField("value");
field.tAccessible(true);
int temp = numA.intValue(); //⾃动拆箱
field.t(numA, numB);
field.t(numB, Integer.valueOf(temp));
}
说明:看到反编译后的代码,验证了我们上⾯说的⾃动装箱拆箱操作 。通过我们对 IntegerCache 内部类的学习可以知
道,Integer.valueOf(1) 和 Integer.valueOf(1) 实际上获取的是 Integer cache[] ⾥⾯的值,具体是 cache 数组⾥⾯的哪个值呢,我们来看 Integer.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.valueOf(1) = IntegerCache.cache[129] ,我们通过查看 IntegerCache 的静态代码块可以知道,IntegerCache.cache[129] = new Integer(1),也就是 value 为
1 的Integer 对象,但是这个 Integer 对象是存在 cache[] 数组缓存⾥⾯的 。既然我们知道了 -128 ~ 127 是使⽤的缓存⾥⾯的 Integer 对象,那么我们进⼀步将代码中的涉及到缓存的 Integer 都替换成缓存对象,添加上注释再来看看执⾏的流程:
public static void main(String args[]) throws Exception {
Integer a = Integer.valueOf(1); //⾃动装箱,Integer.valueOf(1) = IntegerCache.cache[129]
Integer b = Integer.valueOf(2); //⾃动装箱,Integer.valueOf(2) = IntegerCache.cache[130]
System.out.println((new StringBuilder()).append("a = ").append(a).append("b = ").append(b).toString());
swap(a, b);
System.out.println((new StringBuilder()).append("a = ").append(a).append("b = ").append(b).toString());
Integer c = Integer.valueOf(1); //⾃动装箱,Integer.valueOf(1) = IntegerCache.cache[129] 经过 swap ⽅法的修改,此时 value 为 2
System.out.println((new StringBuilder()).append("c = ").append(c).toString());
}
private static void swap(Integer numA, Integer numB) throws Exception {
Field field = java/DeclaredField("value");
field.tAccessible(true);
int temp = numA.intValue();
//numA = Integer.valueOf(1) = IntegerCache.cache[129] ,其 value 为 1
//numB = Integer.valueOf(2) = IntegerCache.cache[130] ,其 value 为 2
//这⼀步是将 IntegerCache.cache[129] 设置为 IntegerCache.cache[130]
field.t(numA, numB);
//numB = Integer.valueOf(2) = IntegerCache.cache[130],其 value 为 2
/
/经过上⼀步的修改,此时 Integer.valueOf(temp) = Integer.valueOf(1) = IntegerCache.cache[129],其 value 为 2
//这⼀步是将 IntegerCache.cache[130] 设置为 IntegerCache.cache[129],IntegerCache.cache[130],其 value 依旧为 2
field.t(numB, Integer.valueOf(temp));
}
说明:也就是说,field.t(numA, numB) 这⼀步实际上修改的是 IntegerCache.cache[129] 的值,⽽ IntegerCache.cache 是全局的,所以会影响到后续使⽤到这个缓存值的所有代码的运⾏结果 。这也就解释了输出结果: a = 2 b = 2 以及 c = 2 的原因了 。但这么做风险太⼤,实际项⽬中不能这么做 。想要完成期望的结果,也就是:a = 2 b = 1,可以使⽤ field.tInt(numB,
Integer.valueOf(temp)) 或者 field.t(numB, new Integer(1)) 这个做法,但是这么做的话,被修改成错误的 cache 值⼜多了⼀个,最终,在错误的道路上⼜前进了⼀步 。
parInt ⽅法
public static int parInt(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 u
* 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 = 0;
boolean negative = fal;
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') { // Possible leading "+" or "-"
翻译工具下载
if (firstChar == '-') {
negative = true;
limit = Integer.MIN_VALUE;
} el if (firstChar != '+')
throw NumberFormatException.forInputString(s);
if (len == 1) // Cannot have lone "+" or "-"
throw NumberFormatException.forInputString(s);
dvdrom是什么意思i++;
}
multmin = limit / radix;
while (i < len) {
/
/ Accumulating negatively avoids surpris near MAX_VALUE
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;
}
} el {
throw NumberFormatException.forInputString(s);
}
return negative ? result : -result;
}
使⽤⽅法: