常用数据类型大家都会用,JDK也为了保持一切皆对象的原则,将8大常用数据类型都封装成了对应的对象,本次先来分析JDK怎么封装的Integer。
首先看Integer的继承关系
是继承Number实现Comparable的,所以Integer也能用compareTo方法进行比较。
Integer 有一个私有属性 value
/**
* The value of the {@code Integer}.
*
* @serial
*/
private final int value;
可以看出这个value是一个final型的,这也解释了为什么我们new 两个同样数字的Integer对象为什么会不是一个对象,当然,也有特殊情况,==比较时会出现相同对象,这是因为Integer类内会给缓存一部分数据,这在下面再具体分析。
接着我们看Integer的构造方法:
public Integer(String s) throws NumberFormatException {
this.value = parseInt(s, 10);
}
public Integer(int value) {
this.value = value;
}
可以发现这两个构造方法是比较简单的,第一个是调用的parseInt方法转化成了Integer对象,关于parseInt方法稍会介绍。
下面分析下Integer是怎么缓存一部分数据的,这就要归功于Integer内部类IntegerCache了。
private static class IntegerCache {
static final int low = -128;
static final int high;
static final Integer cache[]; //缓存cache
static {
// 默认缓存的最大值是127
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); //取配置的和127的大值
//
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的数据
cache[k] = new Integer(j++);
// range [-128, 127] must be interned (JLS7 5.1.7)
assert IntegerCache.high >= 127;
}
private IntegerCache() {}
}
代码里面有注释,还是比较好理解的,分析==比较之前先说下,自动拆箱自动装箱。比如Integer i= 23;JDK会自动将23给我们装箱成Integer对象,当然这是JDK5之后才有的,debug可以看到JDK给我们装箱时是调用的valueOf方法。下面看下这个方法。
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
这个方法可以很好理解,JDK在装箱前会先判断i是否在缓存cache中,如果在的话就直接拿缓存中的数据,如果不在的话才new一个对象。所以当我们这样写Integer i = 23;
Integer m = 23; i== m ,会返回TRUE,这个才是根本原因。
下面说下parseInt(String str);这个方法
public static int parseInt(String s) throws NumberFormatException {
return parseInt(s,10);
}
发现调用的是parseInt(String s,int radix);这个方法
public static int parseInt(String s, int radix) throws NumberFormatException{
//对输入的字符进行判断非空或者是否大于最大或者小于最小,如果是的话,抛异常
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') { // 如果firstChar < '0' 可能会导致 第一个是'+'或者'-'
if (firstChar == '-') {
negative = true;
limit = Integer.MIN_VALUE;
} else if (firstChar != '+') //如果也不是'+'的话,抛异常
throw NumberFormatException.forInputString(s);
if (len == 1) // 不能仅有符号
throw NumberFormatException.forInputString(s);
i++;
}
multmin = limit / radix;溢出判断技巧 ,可在乘法之前判断乘法后是否溢出
while (i < len) {
// 这个方法可以先简单的理解成将string型的转换成int型的稍后介绍这个方法
digit = Character.digit(s.charAt(i++),radix);
if (digit < 0) {
throw NumberFormatException.forInputString(s);
}
if (result < multmin) {
throw NumberFormatException.forInputString(s);
}
result *= radix; //先乘一个radix
if (result < limit + digit) {
throw NumberFormatException.forInputString(s);
}
result -= digit; //再减一个digit得到对应基数的数字
}
} else {
throw NumberFormatException.forInputString(s);
}
return negative ? result : -result; //最终result是一个相对应基数的int 型的数
}
发现上个方法主要是Character.digit(char ch,int radix);这个方法下面我们看一下,这个方法,
public static int digit(char ch, int radix) {
return digit((int)ch, radix); //调用Character类的digit(int codePoint, int radix);方法
}
public static int digit(int codePoint, int radix) {
return CharacterData.of(codePoint).digit(codePoint, radix);//调用CharacterData.of(int codePoint).digjt(int codePoint,int radix);
}
我们查看下CharacterData的of方法:
static final CharacterData of(int ch) {
if (ch >>> 8 == 0) { // 逻辑右移,根据值的大小选择不同的实例此处我们看下第一个
return CharacterDataLatin1.instance;
} else {
switch(ch >>> 16) { //plane 00-16
case(0):
return CharacterData00.instance;
case(1):
return CharacterData01.instance;
case(2):
return CharacterData02.instance;
case(14):
return CharacterData0E.instance;
case(15): // Private Use
case(16): // Private Use
return CharacterDataPrivateUse.instance;
default:
return CharacterDataUndefined.instance;
}
}
}
CharacterDataLatin1.java部分源码
int digit(int ch, int radix) {
int value = -1;
if (radix >= Character.MIN_RADIX && radix <= Character.MAX_RADIX) { //判断基数是否在最大和最小范围内
int val = getProperties(ch); //调用此方法 此方法是得到字符的ASCII值
int kind = val & 0x1F; //value & 11111 这里与过后只可能会是后面的几种 2: 小写字符 1:大写字符 9:数字 20~30 标点 12空格
if (kind == Character.DECIMAL_DIGIT_NUMBER) { //kind == 9 数字
value = ch + ((val & 0x3E0) >> 5) & 0x1F; //value = ch + ((val & 1111100000) >> 5 ) & 11111 关于这一块,本人也是正在学习中,目前没太明白
}
else if ((val & 0xC00) == 0x00000C00) {
// Java supradecimal digit
value = (ch + ((val & 0x3E0) >> 5) & 0x1F) + 10;
}
}
return (value < radix) ? value : -1;
}
int getProperties(int ch) {
char offset = (char)ch;
int props = A[offset]; //将A数组进行
return props;
}
截止到现在parseInt(String s);方法应该是完了。感觉JDK封装的挺厉害的,还是应该看源码学习比较好。下面看Integer.toString(int i);方法。
public static String toString(int i) {
if (i == Integer.MIN_VALUE)
return "-2147483648";
int size = (i < 0) ? stringSize(-i) + 1 : stringSize(i);
char[] buf = new char[size];
getChars(i, size, buf);
return new String(buf, true);
}
此处判断数字的size时感觉非常好。我们看下stringSize的方法。直接就可以得到int类型的数有几位
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;
}
static void getChars(int i, int index, char[] buf) {
int q, r;
int charPos = index;
char sign = 0;
if (i < 0) {
sign = '-';
i = -i;
}
// 每次生成两个
while (i >= 65536) {
q = i / 100;
// really: r = i - (q * 100);
r = i - ((q << 6) + (q << 5) + (q << 2));
i = q;
buf [--charPos] = DigitOnes[r];
buf [--charPos] = DigitTens[r];
}
// Fall thru to fast mode for smaller numbers
// assert(i <= 65536, i);
for (;;) {
q = (i * 52429) >>> (16+3); //这里用52429 好像是因为这个数进行乘除精确度最高
r = i - ((q << 3) + (q << 1)); // r = i-(q*10) ...
buf [--charPos] = digits [r];
i = q;
if (i == 0) break;
}
if (sign != 0) {
buf [--charPos] = sign;
}
}
今天就先到这里吧!!!有时间继续。。。