常年与Java打交道,总会遇到各种问题,都一一记录下来了。
为督促自己的学习,也为了与大家分享,写下本文,并持续更新。
Object中的方法
private static native void registerNatives()
,本地方法,在Object
类初始化的时候注册其他native
方法hashCode
、wait
、notify
、notifyAll
、clone
,源码位于Object.c
public final native Class<?> getClass()
,从oop
的_metadata
中读取_klass
或_compressed_klass
public native int hashCode()
,返回对象的identity hash code
。具体实现还和锁有关:当一个对象已经计算过identity hash code,它就无法进入偏向锁状态
当一个对象当前正处于偏向锁状态,并且需要计算其identity hash code的话,则它的偏向锁会被撤销,并且锁会膨胀为重量锁;
重量锁的实现中,ObjectMonitor类里有字段可以记录非加锁状态下的mark word,其中可以存储identity hash code的值。或者简单说就是重量锁可以存下identity hash code。
public boolean equals(Object obj)
,需要满足自反性、对称性、传递性、一致性。protected native Object clone() throws CloneNotSupportedException
,由虚拟机实现的对象拷贝方法。对于没有实现java.lang.Cloneable
接口的对象,JVM有能力拷贝,但是拒绝拷贝。拷贝时先开辟一块相等的内存空间,然后以jlong
(8字节)为原子,从头按顺序拷贝内容。期间原始对象的内容可能改变,因此clone()
不是线程安全的,需要自行加锁。但是因为对象内部的所有数据都是以8字节对齐的,所以不会出现只拷贝了半个引用或者只拷贝了半个double
的情况。public String toString()
,人可读的描述信息。public final native void notify()
public final native void notifyAll()
public final native void wait() throws InterruptedException
,调用这三个方法的位置都必须同步,并且执行时都会导致偏向锁膨胀为重量级锁。protected void finalize() throws Throwable
,实现了这个方法的对象会在初始化时被包装成java.lang.ref.Finalizer
塞到Finalizer
维护的链表unfinalized
中。通过实现这个方法,对象可以在被垃圾回收之前做些事情,比如可以发送一些通知,或者避免自己被垃圾回收,且这个机会最多只有一次,因为这个对象不可能再次进入链表中了。其实finalize()
的实现和软、弱、虚引用有些相似,都是java.lang.ref.Reference
的子类,通过引用队列实现的。
虚引用实践
整理为博客《弱引用、虚引用、finalize实践,及它们的顺序》
WeakHashMap的实现
WeakHashMap
的Entry
继承自WeakReference
,且其referent
为entry的key,也就意味着,只要一个entry的key被回收,这个entry就被加入到引用队列中。在WeakHashMap
的get()
、put()
、forEach()
、size()
、remove()
等诸多方法调用的时候都会调用到一个expungeStaleEntries()
方法,这个方法就会遍历引用队列来清理无用的entry。
try-return-finally-return
// 返回值是0
private static int foo1() {
int i = 0;
try {
return i; // 生效
} finally {
i++;
}
}
// 返回值是1
private static int foo2() {
int i = 0;
try {
return i;
} finally {
return ++i; // 生效
}
}
// 返回值是1
private static int foo3() {
try {
return 0;
} finally {
return 1; // 生效
}
}
总结:finally
中的赋值不会改变try
中已经计算好的返回值,但是finally
中的return
会截胡。
编译器常量
final
的字面量在编译时会被替换为常量。包括final
的类属性、final
的本地变量。
Arrays.asList(array)的返回值和数组绑定且不可修改?
这个竟然是真的。。。。
Arrays.asList()
的返回值不是java.util.ArrayList
,而是java.util.Arrays$ArrayList
,它没有重写任何定义在AbstractList
中的新增和删除有关的方法,因此尝试新增或删除时会调用到AbstractList
中的版本,直接抛出异常。
原地排序、修改以及读取,这些底层数组支持的操作都是支持的。
接口和抽象类的区别
Java8和Java9对接口的改变太大了,最近时常会疑惑,为什么接口和抽象类越来越像了。。
其实还是很多“底线”没有突破的,比如单继承和多实现。
下面整理一下,接口简写为I
,抽象类简写为A
。
A
既可以声明abstract
方法,又可以定义任意可见性的带方法体的方法,I
只能声明public abstract
方法
- Java8开始
I
可以声明default
方法,也可以声明static
方法 - 预计Java9开始
I
可以声明private
方法
- Java8开始
A
可以声明变量,I
只能定义常量A
可以extends A
,也可以implements I
;I
只能extends I
- 一个类只能
extends
一个A
,但可以implements
多个I
A
有constructor
,I
没有
顺序读写/随机读写 RandomAccessFile
常规使用Reader
、Writer
、InputStream
、OutputStream
读写文件都是顺序读写,进行随机读写需要使用java.io.RandomAccessFile
。
20170513 最后更新