### Integer
> 本文源码基于JDK8
Integer也是我们经常使用的工具类、包装类,此文主要用于记录学习笔记,主要从源码角度深入了解一下。
```
public final class Integer extends Number implements Comparable<Integer> {
// 2147483647
public static final int MIN_VALUE = 0x80000000;
// -2147483648
public static final int MAX_VALUE = 0x7fffffff;
// 获取基本类型int类型是class
public static final Class<Integer> TYPE = (Class<Integer>) Class.getPrimitiveClass("int");
}
```
说明:Integer.TYPE == int.class // true
* 继承Number类
Number类中只有构造方法和几个抽象方法:
![](https://img.kancloud.cn/3e/58/3e583f4da8f43b54f3e44e7be5f1ef10_662x326.png)
### 构造方法
```
public Integer(int value) {
this.value = value;
}
public Integer(String s) throws NumberFormatException {
this.value = parseInt(s, 10);
}
```
### 自动装箱和自动拆箱
JDK1.5之后,java提供了自动装箱和自动拆箱的功能,下面从源码角度分析下Integer的装箱:
```
package com.quancheng;
public class ClassTest {
public static void main(String[] args) throws InterruptedException {
Integer num = 10;
}
}
```
通过javap -v ClassTest.class查看字节码,可以看出自动装箱实际是JVM编译器帮我们做的工作,调用的是Integer.valueOf\(\)方法
```
public static void main(java.lang.String[]) throws java.lang.InterruptedException;
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=1, locals=2, args_size=1
0: bipush 10
2: invokestatic #2 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
5: astore_1
6: return
```
分析到这里可以看出,实际上Integer a = 100 等价于Integer a = Integer.valueOf\(100\);只不过这个工作是JVM帮我们做的;
自动拆箱:
```
public static void main(String[] args) throws InterruptedException {
int num = new Integer(11);
}
```
对应的字节码指令:
```
public static void main(java.lang.String[]) throws java.lang.InterruptedException;
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=3, locals=2, args_size=1
0: new #2 // class java/lang/Integer
3: dup
4: bipush 11
6: invokespecial #3 // Method java/lang/Integer."<init>":(I)V
9: invokevirtual #4 // Method java/lang/Integer.intValue:()I
12: istore_1
13: return
```
通过分析可以发现,自动拆箱实际是编译器调用了Integer.intValue\(\)的方法完成的转换
### 重点方法
* 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是一个静态内部类,主要用于缓存low - high之间数字的包装类
```
private static class IntegerCache {
static final int low = -128;
static final int high;
static final Integer cache[];
static {
// high value may be configured by property
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);
// 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;
}
private IntegerCache() {}
}
```
上面的源码中可以看出IntegerCache有三个被final修饰的静态filed外加一个静态块和一个私有的构造器;很简单很普通的一个类,被缓存的包装类就介于low - high之间,low的值已经写死-128,而high的值由你的虚拟机决定sun.misc.VM.getSavedProperty\("java.lang.Integer.IntegerCache.high"\),既然是一个参数也就意味着你可以动态设置,具体怎么设置自行百度。然后在循环中将low - high之间数字的装箱后方法cache\[\]这个Integer类型的数组中。这样就完成了缓存
在日常编码中,我们需要注意Integer缓存的问题
```
Integer a = 100;
Integer b = 100;
Integer f = Integer.valueOf(100);
Integer c = 200;
Integer d = 200;
System.out.println(a == b);//true
System.err.println(a == f); // true
System.out.println(c == d);//false
```
* stringSize\(int x\)
这个方法很有意思,我觉得可以单独看看这个方法,主要作用就是判断一个数字的位数,但是运用的非常巧妙
```
final static int [] sizeTable = { 9, 99, 999, 9999, 99999, 999999, 9999999,
99999999, 999999999, Integer.MAX_VALUE };
// Requires positive x
static int stringSize(int x) {
for (int i=0; ; i++)
if (x <= sizeTable[i])
return i+1;
}
```
* equals\(Object obj\)
需要注意的是Integer也重写了equals\(Object obj\),故若比较的值类型都是Integer时,equals\(\)和==作用是相同的
```
public boolean equals(Object obj) {
if (obj instanceof Integer) {
return value == ((Integer)obj).intValue();
}
return false;
}
```
```
我们可以写个例子测试下:
Integer num = new Integer(999);
System.err.println(num ==999);
public static void main(java.lang.String[]) throws java.lang.InterruptedException;
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=3, locals=2, args_size=1
0: new #2 // class java/lang/Integer
3: dup
4: sipush 999
7: invokespecial #3 // Method java/lang/Integer."<init>":(I)V
10: astore_1
11: getstatic #4 // Field java/lang/System.err:Ljava/io/PrintStream;
14: aload_1
15: invokevirtual #5 // Method java/lang/Integer.intValue:()I
18: sipush 999
21: if_icmpne 28
24: iconst_1
25: goto 29
28: iconst_0
29: invokevirtual #6 // Method java/io/PrintStream.println:(Z)V
32: return
```
可以看出实际上是先拆箱再比较值
### 总结
* Integer i1=40;Java在编译的时候会直接将代码封装成Integer
* Integer i1=Integer.valueOf\(40\);,从而使用常量池中的对象
* Integer i1 = new Integer\(40\);这种情况下会创建新的对象
* Integer的toString方法分成了两部分进行处理,大于等于65536和小于65536的部分
### Integer有趣示例
```
Integer i1 = 40;
Integer i2 = 40;
Integer i3 = 0;
Integer i4 = new Integer(40);
Integer i5 = new Integer(40);
Integer i6 = new Integer(0);
System.out.println("i1=i2 " + (i1 == i2));
System.out.println("i1=i2+i3 " + (i1 == i2 + i3));
System.out.println("i1=i4 " + (i1 == i4));
System.out.println("i4=i5 " + (i4 == i5));
System.out.println("i4=i5+i6 " + (i4 == i5 + i6));
System.out.println("40=i5+i6 " + (40 == i5 + i6));
output==>
i1=i2 true
i1=i2+i3 true
i1=i4 false
i4=i5 false
i4=i5+i6 true
40=i5+i6 true
```
解释:语句i4 == i5 + i6,因为+这个操作符不适用于Integer对象,首先i5和i6进行自动拆箱操作,进行数值相加,即i4 == 40。然后Integer对象无法与数值进行直接比较,所以i4自动拆箱转为int值40,最终这条语句转为40 == 40进行数值比较
- java演变
- JDK各个版本的新特性
- JDK1.5新特性
- JDK1.6新特性
- JDK1.7新特性
- JDK1.8新特性
- JAVA基础
- 面向对象特性
- 多态
- 方法重载
- 方法重写
- class
- 常量
- 访问修饰符
- 类加载路径
- java-equals
- 局部类
- java-hashCode
- Java类初始化顺序
- java-clone方法
- JAVA对象实例化的方法
- 基础部分
- JAVA基础特性
- JAVA关键字
- javabean
- static
- 日期相关
- final
- interface
- 函数式接口
- JAVA异常
- 异常屏蔽
- try-with-resource资源泄露
- JAVA引用
- WeakReference
- SoftReference
- PhantomReference
- 位运算符
- try-with-resource语法糖
- JDK冷知识
- JAVA包装类
- JAVA基本类型与包装类
- java.lang.Boolean
- java.lang.Integer
- java.lang.Byte
- java.lang.Short
- java.lang.Long
- java.lang.Float
- java.lang.Double
- java.lang.Character
- 日期相关
- TemporalAdjusters
- String
- 字符串常量池
- String拼接
- String编译期优化
- StringBuilder&StringBuffer
- intern
- 注解
- java标准注解
- 内置注解
- 元注解
- 自定义注解
- 注解处理器
- JVM注解
- Java8 Annotation新特性
- 反射-Reflective
- Reflection
- Class
- Constructor
- Method
- javabean-property
- MethodHandles
- 泛型
- 类型擦除
- bridge-method
- Accessor&Mutator方法
- enum
- JAVA数组
- finalize方法
- JAR文件
- JAVA高级编程
- CORBA
- JMX
- SPI
- Java SPI使用约定
- ServiceLoader
- 实际应用
- IO
- 工具类
- JDK常用工具类
- Objects
- System
- Optional
- Throwable
- Collections
- Array
- Arrays
- System
- Unsafe
- Number
- ClassLoader
- Runtime
- Object
- Comparator
- VarHandle
- 数据结构
- 栈-Stack
- 队列(Queue)
- Deque
- PriorityQueue
- BlockingQueue
- SynchronousQueue
- ArrayBlockingQueue
- LinkedBlockingQueue
- PriorityBlockingQueue
- ConcurrentLinkedQueue
- 列表
- 迭代器
- KV键值对数据类型
- HashMap
- TreeMap
- Hash冲突
- ConcurrentHashMap
- JDK1.7 ConcurrentHashMap结构
- jdk7&jdk8区别
- 集合
- Vector
- Stack
- HashSet
- TreeSet
- ArrayList
- LinkedList
- ArrayList && LinkedList相互转换
- 线程安全的集合类
- 集合类遍历性能
- 并发容器
- CopyOnWriteArrayList
- ConcurrentHashMap
- 同步容器
- BitMap
- BloomFilter
- SkipList
- 设计模式
- 设计模式六大原则
- 单例模式
- 代理模式
- 静态代理
- 动态代理
- JDK动态代理
- cglib动态代理
- spring aop
- 策略模式
- SpringAOP策略模式的运用
- 生产者消费者模式
- 迭代器模式
- 函数式编程
- 方法引用
- 性能问题
- Lambda
- Lambda类型检查
- Stream
- findFirst和findAny
- reduce
- 原始类型流特化
- 无限流
- 收集器
- 并行流
- AOP
- 静态织入
- aspect
- aspect的定义
- AspectJ与SpringAOP
- 动态织入
- 静态代理
- 动态代理
- JDK动态代理
- CGLib动态代理
- Spring AOP
- SpringAOP五种通知类型
- @Before
- @AfterReturning
- @AfterThrowing
- @After
- @Around
- Aspect优先级
- SpringAOP切点表达式
- within
- execution
- 嵌套调用
- 系统优化与重构
- 重叠构造器模式
- 工具类构造器优化
- 常见面试题
- new Object()到底占用几个字节
- 访问修饰符
- cloneable接口实现原理
- 异常分类以及处理机制
- wait和sleep的区别
- 数组在内存中如何分配
- 类加载为什么要使用双亲委派模式,有没有什么场景是打破了这个模式
- 类的实例化顺序
- 附录
- JAVA术语
- FAQ
- 墨菲定律
- 康威定律
- 软件设计原则
- 阿姆达尔定律
- 字节码工具
- OSGI