此系列博文主要记录在阅读Jdk源码过程中遇到的问题及一些小的知识点,如理解有误之处,可留言提出。
Object 类
clone
如果一个类仅包含基本字段或对不可变对象的引用,通常情况下,clone后的对象可认为是深拷贝,若包含对象的引用,如果要实现深拷贝,那么对象本身需要实现clone方法,修改返回的对象中的对象引用。
equal
reflexive(自反)symmetric(对称)transitive(传递)consistent(一致)
hashcode
If two objects are equal according to the {@code equals(Object)} method,
then calling the {@code hashCode} method on each of the two objects must produce the same integer result.
wait
notify
notifyAll
toString
finalize
String类
String implements java.io.Serializable, Comparable<String>, CharSequence
private final char value[]; 不可变字符序列
重点:
1.Sting 对象的两种创建方式
https://www.cnblogs.com/developer_chan/p/8608410.html
2.拼接时如果有对象:
String a = "a"
String d = "ab"
String b = a + "b" ==> d == b false
创建b时是直接在堆上创建,b指向的是堆中对象的内存地址 (因为包含对象,所以是在运行时创建)
String c = "a" + "b" d == c true (编译时创建)
3.intern方法
String f = d.intern() 如果字符串常量池中有指向某个对象(e)的引用e.equals(d) 那么f指向与e指向同一个对象
如果没有,那么先在堆上创建,然后放入常量池中,再返回一个对象的引
4.hashcode()
使用31作为一个乘子(是个大小合适的质数)
for (int i = 0; i < value.length; i++) {
h = 31 * h + val[i];
}
hash = h;
5.静态方法String.valueOf (以public static String valueOf(int i) 举例说明)
调用了Integer.toString(i)
public static String toString(int i) {
........
return new String(buf, true);
}
每次返回的时在堆中新创建的String对象
所以:
String a = String.valueOf(1);
String b = String.valueOf(1);
a == b ==> fasle
a.equals(b) ==> true
AbstractStringBuilder类
AbstractStringBuilder implements Appendable, CharSequence
char[] value; 可变字符序列
重点:
1.expandCapacity 扩容为原length*2 + 2 如果 < minimumCapacity 则扩容为 minimumCapacity
int newCapacity = value.length * 2 + 2;
if (newCapacity - minimumCapacity < 0)
newCapacity = minimumCapacity;
2.主要子类 StringBuffer StringBuilder
区别:StringBuffer多线程安全(使用 synchronized),StringBuilder线程不安全
StringBuffer 锁的是StringBuffer对象并非源char[],作为参数传递的时候(多线程情况下源char[]会被修改的风险)
主要方法 append insert
Integer类
Integer extends Number implements Comparable<Integer>
1.
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");
私有的静态内部类IntegerCache 持有一个静态的Integer[] 作为缓存 可以通过java.lang.Integer.IntegerCache.high 调整最大的缓存范围
2.
public static int highestOneBit(int i) {
// HD, Figure 3-1
i |= (i >> 1);
i |= (i >> 2);
i |= (i >> 4);
i |= (i >> 8);
i |= (i >> 16);
return i - (i >>> 1);
}
整体思想:将i最高位往后的所有bit位置为1,然后无符号右移做减法运算
获取最高位的数值
3.
public static int lowestOneBit(int i) {
// HD, Section 2-1
return i & -i;
}
获取最低位的数值
4.
public static int numberOfLeadingZeros(int i) {
// HD, Figure 5-6
if (i == 0)
return 32;
int 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;
}
获取二进制前导0的个数
5.
public static int numberOfTrailingZeros(int i) {
// HD, Figure 5-14
int y;
if (i == 0) return 32;
int n = 31;
y = i <<16; if (y != 0) { n = n -16; i = y; }
y = i << 8; if (y != 0) { n = n - 8; i = y; }
y = i << 4; if (y != 0) { n = n - 4; i = y; }
y = i << 2; if (y != 0) { n = n - 2; i = y; }
return n - ((i << 1) >>> 31);
}
获取二进制后置0的个数
6.
public static int bitCount(int i) {
// HD, Figure 5-2
i = i - ((i >>> 1) & 0x55555555);
i = (i & 0x33333333) + ((i >>> 2) & 0x33333333);
i = (i + (i >>> 4)) & 0x0f0f0f0f;
i = i + (i >>> 8);
i = i + (i >>> 16);
return i & 0x3f;
}
计算二进制位上为1的位数
不能理解:rotateLeft rotateRight reverse signum reverseBytes
下面我们进行一个归类:
Integer派别:Integer、Short、Byte、Character、Long这几个类的valueOf方法的实现是类似的。
缓存范围:
Byte,Short,Integer,Long为 -128 到 127
Character范围为 0 到 127
Double派别:Double、Float的valueOf方法的实现是类似的。每次都返回不同的对象。
ThreadLocal类
在线程内部存在一个ThreadLocal.ThreadLocalMap ,ThreadLocalMap 是TreadLocal内部的一个静态类,持有一个Entry[]
里面存储的Entry对象以变量ThreadLocal对象(是个弱引用,Entry继承自WeakReference,会在YGC时被回收))的hashcode为key,想要存储的变量为value。
问题1:为啥使用弱引用?
分两种情况:
1.使用强引用的话,如果持有ThreadLocal的对象被回收,但是由于还有ThreadLocalMap在引用ThreadLocal对象,那么在线程
存活的时候,ThreadLocal对象本身不会被回收,造成内存泄露。
2.使用弱引用,持有ThreadLocal的对象被回收的话,在YGC的时候ThreadLocalMap引用ThreadLocal为弱引用,ThreadLocal就会
被彻底回收,从而避免的ThreadLocal对象造成的内存泄露,存储在ThreadLocalMap中key为null的Entry会在调用set get remove 方法时
被回收。
问题2:为啥推荐使用完后主动调用remove方法?
情形1:
将ThreadLocal声明为static类型的变量,延长了ThreadLocal对象的生存周期,这样的话,GC的时候Entry对象也同样不会被回收。调用set get方法也不会
(set get方法清除的是key为null的Entry对象),造成内存泄露。
情形2:
调用ThreadLocal set方法后,持有ThreadLocal对象的对象被回收后,再也没有调用过set get 方法,这时Entry的key即使为null也不会被
回收,造成内存泄露。