toString()的源代码如下:
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
getClass() 返回类对象 ;
getName() 以String形式返回类对象的名称(包换包名);
Integer.toHexString(hashCode()) 以对象的哈希码为参数,以16进制无符号整数形式返回此哈希码的字符串表示形式;
下面主要分析下Integer.toHexString()
以下是其源码:
public static String toHexString(int i) {
return toUnsignedString0(i, 4);
}
private static String toUnsignedString0(int val, int shift) {
// assert shift > 0 && shift <=5 : "Illegal shift value";
int mag = Integer.SIZE - Integer.numberOfLeadingZeros(val); // (1)
int chars = Math.max(((mag + (shift - 1)) / shift), 1); // (2)
char[] buf = new char[chars];
formatUnsignedInt(val, shift, buf, 0, chars); // (3)
// Use special constructor which takes over "buf".
return new String(buf, true);
}
(1) 这一步主要是为了计算val值对应的二进制数中除去首部0的个数后剩下的有效位数mag。
a.Integer.SIZE: int在jvm中占4个字节,共32位;
b.首部0的个数:是指从左边第一个位置开始累加0的个数,一直加到第一个非零值;
c.方法numberOfLeadingZeros() 就是计算首部0的个数。
下面看看numberOfLeadingZeros()的源码,源码应用了二分查找,先把32位整形分为高16位和低16位查找非零数,在对高16位进行或低16位进行二分,以此类推,直到找到左边第一个非零值的位置。
public static int numberOfLeadingZeros(int i) {
// HD, Figure 5-6
if (i == 0)
return 32;
// 初始首部0的个数:1 假定最高位有一个0,最后的时候会根据最高位是否位零补偿返回
int n = 1;
// 下面的代码主要是为了找出左边第一个非零值的位置
// 采用二分查找的方法,首先将 i 无符号右移16位后,有两种情况(a, b):
// a. 右移后 = 0,则第一个非零值出现在低16位,那么 i 首位至少有16个0(n=17),
// 同时将 i 左移16位后赋值给自己(将低16位移到了高16位,这样可以使两种保持同一的状态进行后续判断);
// b. 右移后!=0,则第一个非零值出现在高16位(n=1),继续在高16位中寻找;
// 将高16位继续分为 高8位和低8位进行判断,一直二分到还有两位的时候;
// 最后 i 无符号右移31位(结果要么是0要么是1),如果右移后为0,说明此时的最高位为0,无需补偿,直接返回 n;
// 如果右移后是1,则说明最高位不为0,需补偿,返回 n-1;
if (i >>> 16 == 0) { n += 16; i <<= 16; }
if (i >>> 24 == 0) { n += 8; i <<= 8; }
if (i >>> 28 == 0) { n += 4; i <<= 4; }
if (i >>> 30 == 0) { n += 2; i <<= 2; }
n -= i >>> 31;
return n;
}
(2) int chars = Math.max(((mag + (shift - 1)) / shift), 1):计算 i 的有效二进制的位数所需要占的字符数(即对应的16进制的有效位数),toString()方法是以16进制返回,这里就以16进制来解释。
根据二进制和16进制的转化关系可以知道,每4个二进制位代表一个16进制数,mag是有效的二进制位个数,所以想要计算需要的字符数可能遇到如下三种情况:
a. mag=0,那么只需要一个字符即可;
b. mag !=0, 是4的整数倍,则需要mag/4 个字符;
c. mag !=0,不是4的整数倍,则需要mag/4 +1 个字符;
(mag + (shift - 1)) / shift 可以解决后面两种情况;
Math.max(((mag + (shift - 1)) / shift), 1) 可以解决这三种情况。
(3) 在toString() 方法中,formatUnsignedInt() 完成的功能就是将val值以16进制数形式存进buf数组中。
先进行按位与运算,算出val的二进制中低四位对应的10进制的值,在 digits 数组中获取对应的16进制值,存进buf字符数组中;后将val无符号右移4位,保持在最低四位与mask进行按位与运算,在 digits 数组中获取对应的16进制值,存进buf字符数组中,以此循环,直到val=0或者charPos<=0后退出循环。
static int formatUnsignedInt(int val, int shift, char[] buf, int offset, int len) {
int charPos = len;
int radix = 1 << shift;
int mask = radix - 1;
do {
buf[offset + --charPos] = Integer.digits[val & mask];
val >>>= shift;
} while (val != 0 && charPos > 0);
return charPos;
}
/**
* 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 数组中表示 16 进制的是前 16 个,数组下标最大为 15, Integer.digits[val & mask] 保证了每次从最低四位开始匹配对应的 16 进制数。
总的来说,分为了如下步骤:
(1)计算 hashCode 对应的有效二进制位数 n
(2)n 位二进制数对应的 16 进制数的个数,即对应的所要占据的字符数
(3)将 n 位二进制数转换为 16 进制数并存入字符数组中
(4)最后返回 16 进制无符号整数的字符串
如有疑问,欢迎沟通~