int 是 Java 八大原始类型之一,是 Java 语言中为数不多不是对象的东西,Integer 是 int 的包装类,里面使用了一个 int
类型的变量来存储数据,提供了一些整数之间的常用操作,常规性的介绍就这么一点,程序员不喜欢说,程序员就喜欢源码,我们还是来看源码吧
* @author Lee Boynton * @author Arthur van Hoff * @author Josh Bloch *
@author Joseph D. Darcy * @since JDK1.0 */ public final class Integer extends
Number implements Comparable<Integer> { /** * 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; /** * The value of the {@code Integer}. * * @serial */
private final int value; /** * 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); }
上面这段源码是我截取出来的,在 Integer 类中,这些代码不是连在一起的,把他们放在一起,那是因为我想说明点事情,我们仔细看看这段代码,Integer
类是被 final ,这说明了什么?用于存放变量的 value 也被 private final 修饰,这又说明了什么?看着这些是不是有点熟悉呢?
没错,String 对象也是这样的,这说明 Integer 对象也是不可变的,所以以后如果被问到 Integer 对象是不是不可变对象时,记得回答是喔。为什么
Integer 对象也会设计成不可变对象呢?其实我也不知道,我没有从文档中找到答案,但是在杨晓峰老师的文章中看到过有关说明,杨晓峰老师说 Integer
类设计成不可变跟 getInteger() 方法有关系,getInteger()方法的源码如下:
public static Integer getInteger(String nm, Integer val) { String v = null;
try { v = System.getProperty(nm); } catch (IllegalArgumentException |
NullPointerException e) { } if (v != null) { try { return Integer.decode(v); }
catch (NumberFormatException e) { } } return val; }
getInteger() 方法是用来获取系统属性的,我们通过属性来设置服务器的某个服务器的端口,如果 Integer
可变的话,那么我们就能够轻易的改变这个属性的值,这会使得我们的产品存在安全风险。

上面我们我么简单的聊了一下 Integer 类的实现,聊到 int 与 Integer,自然就少不了自动装箱和自动拆箱。

1、自动装箱、拆箱

自动装箱和拆箱是从 JDK 1.5 开始引进的功能,它是一种语法糖,Java 可以根据上下文,自动的在原始类型和包装类型中进行转换,简单的来说就是 Java
平台保证了不同的写法通过编译之后会产生相同的字节码,保证了运行时是等价的。自动装箱和拆箱极大地简化了相关编程。

自动装箱:将原始类型转化为包装类型的过程

比如将 int 类型转换成 integer
类型,这就是原始类型到包装类型的转变,在编译的时候,编译器就会帮我们做自动转换,这个过程对我们程序员来说是无感知的,例如这段给 Integer 对象赋值的代码:
Integer x = 3000;
这段代码经过编译器之后,会转换成下面这段代码:
Integer x = Integer.valueOf(3000);
这就是自动装箱过程,这个过程你是不知道的,所以它才叫自动装箱,在自动装箱的过程中使用到了 valueOf() 方法,来看看 JDK 中这个方法的源码:
public static Integer valueOf(int i) { if (i >= IntegerCache.low && i <=
IntegerCache.high) return IntegerCache.cache[i + (-IntegerCache.low)]; return
new Integer(i); }
这个方法里,前面先进行了一个缓存判断,如果你不知道的话,先忽略掉它,最后返回了 new Integer(i) 对象引用,这个方法就是帮你去调用了
Integer 类的构造器。这就是自动装箱。

自动拆箱:将包装类型转换成原始类型的过程

将 Integer 类型转换为 Int
类型,这是一个包装类型转成成原始类型的过程,在这个过程中就会涉及到自动拆箱。来看看这段代码(这是一段很操蛋的代码,实际中应该没人这样写):
Integer mm = 1000; int mmm = mm;
在编译的时候,这段代码会被编译器编译成下面这段代码:
Integer mm = Integer.valueOf(1000); int mmm = mm.intValue();
主要看int mmm = mm.intValue();这行代码,这行代码跟我们写的不一样了,使用到了一个 intValue() 方法,来看看 Integer
类中 intValue() 方法的源码:
/** * Returns the value of this {@code Integer} as an * {@code int}. */ public
int intValue() { return value; }
这个方法的作用就是把 Integer 对象中用来存储值的 value
变量返回了,这就是自动拆箱,好了,关于自动装箱和自动拆箱我们都了解了,还记得自动装箱过程中涉及到的缓存吗?接下来我们一起了解一下。

2、Integer 缓存策略

在自动装箱的 valueOf() 方法中,我们看到了有一个缓存判断的操作,是的,Integer
类中有缓存池,会将使用频繁的值缓存起来,以便提高系统的使用性能,在自动装箱的过程中,会先判断该值是否存在缓存池中,如果存在直接从缓存池中取出引用返回,如果不存在则调用构造函数构造对象。缓存是自动装箱操作独享的,直接通过构造函数构造出来的
Integer 对象即使值在缓存范围内,也不会使用到缓存池。在 Integer 类中,使用了一个内部类来实现缓存,这个内部类叫做
IntegerCache,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[]; 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() {} }
从源码和 Java 注释中我们可以看出 IntegerCache 的缓存默认值范围 -128 ~ 127 。但是我们也可以在启动时通过 JVM
命令来设置缓存范围的最大值,只需要在启动时添加 -XX:AutoBoxCacheMax=
参数就可以了,但是记得这个 size 可不要乱设置,需要全方位考虑,比如你设置成 10 万,那么这 10
万个数都会在启动刚启动时就添加到内存中,想想这会占用你多少内存?这样做就得不偿失了,Java 公司设置成 -128 ~ 127
是有道理的,发现大部分人使用的值都在 -128 ~ 127 之间,这些值占用的内存比较少,性能上比通过构造函数构造对象要好不少。如何你使用的 Integer
的值在缓存范围的话,就用 Integer i = value 的形式构建对象,如果你的值不在缓存范围内,则使用 Integer i = new
Integer(value) 的形式构建 Integer 对象,避免自动装箱的过程。最后我们来看一下 Integer 对象比较常用的方法 parseInt 方法

3、parseInt() 方法

parseInt() 方法的作用是用来将整数型的字符串转换成整数,parseInt 方法需要和 valueOf
方法区分开来,有不少人会问这两方法有什么区别,最后度会返回 int 类型,都能将整数型的字符串转换成整数型,比如这段代码
System.out.println(Integer.parseInt("+12"));
System.out.println(Integer.valueOf("+12"));
最后都会输出 12 ,输出的结果相同是因为 valueOf 方法使用中会调用 parseInt 方法将整数型字符转换为整数,并且会在内存中创建一个值为 12
的 Integer 对象,然后返回这个对象引用。而 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 = 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') { // Possible
leading "+" or "-" if (firstChar == '-') { negative = true; limit =
Integer.MIN_VALUE; } else if (firstChar != '+') throw
NumberFormatException.forInputString(s); if (len == 1) // Cannot have lone "+"
or "-" throw NumberFormatException.forInputString(s); i++; } multmin = limit /
radix; while (i < len) { // Accumulating negatively avoids surprises 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; }
} else { throw NumberFormatException.forInputString(s); } return negative ?
result : -result; }
在调用 parseInt 方法时,我们可以传入一个 radix 变量,用来告诉它使用什么进制来进行转换,默认使用的是十进制。

文章不足之处,望大家多多指点,共同学习,共同进步

最后

打个小广告,欢迎扫码关注微信公众号:「平头哥的技术博文」,一起进步吧。