面试官:Object里都有哪些方法?我:hashCodeequalstoStringwaitnotifyclonefinalize。面试官:今天的面试就到这儿了~
Posted 徐同学呀
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了面试官:Object里都有哪些方法?我:hashCodeequalstoStringwaitnotifyclonefinalize。面试官:今天的面试就到这儿了~相关的知识,希望对你有一定的参考价值。
Object里都有哪些方法?
问题看着简单,但是考察面很广,每一个方法要扩展开来的话都可以单独成为一篇文章。
主要考察几个知识点:
hashCode
、equals
的使用,实际开发中有没有重写过之类的。wait
、notify
搭配synchronized
使用,还可以扯到synchronized
上,包括synchronized
底层实现、锁的升级,以及与AQS的对比。clone
,浅克隆和深克隆,浅克隆需要注意什么?深克隆如何实现,又可以扯到jdk的序列化,以及开源的序列化方案。finalize
,重写finalize
注意事项,什么时候会调用finalize
,涉及到垃圾回收器GC Roots
标记过程,扯到这里就可以继续扯垃圾收集算法、垃圾回收器了,进而扯到 jvm 的调优。
hashCode、equals、toString
Object是所有类的超类,它里面常见的几个方法 hashCode
、equals
、toString
,这几个方法也是平时在子类中最常重写的方法。
wait、notify
还有wait
、notify
方法,需要和 synchronized
搭配使用,可以实现一个简易的 生产者-消费者模式。
clone
还有 clone
,finalize
,这两个方法都是protected
修饰的,不能直接调用,子类想调用这两个方法必须重写。
1、先说一下clone
方法,子类重写clone
方法,可以直接调用父类Object
的clone
,它是个native方法,它的克隆算是浅克隆,虽然克隆出来的实例对象是一个新对象,但是如果原实例对象里有引用对象是共享的,意思就是修改原对象中的引用对象中的属性值,克隆对象中的引用对象的属性值也会被修改。
2、调用父类Object 的clone还需要注意,子类必须实现一个接口 Cloneable
,它就是一个空接口,里面没有方法定义,目的就是标识这个类支持克隆,如果没有实现接口Cloneable,就会抛一个 CloneNotSupportedException
异常。
3、如果想实现深克隆,就得自己实现克隆的逻辑,比如自己new一个新对象,然后把引用对象也new一下。
4、还可以通过序列化和反序列化实现深克隆,序列化和反序列化可以直接用jdk自带的 ObjectOutputStream
和 ObjectInputStream
实现,不过需要注意,序列化的类以及其类的实例对象里的引用对象对应的类都必须实现接口 Serializable
,它也是一个空接口,目的也是标识 该类 支持序列化和反序列化。
5、说到接口Serializable
,就不得不说一下 serialVersionUID
,序列化和反序列化的过程中,每个可序列化的类都会关联一个版本号,就是 serialVersionUID
,在反序列化的时候,会验证序列化对象的发送方和接收方的serialVersionUID
是否相等,相等就可以反序列化,不相等就会反序列化失败(InvalidClassException
异常)。
6、如果不显式声明serialVersionUID
,则序列化运行时将根据类的各个方面计算该类的默认serialVersionUID
值,类似于md5和checksum的作用。但是强烈建议显式声明serialVersionUID
,因为缺省的serialVersionUID
计算的默认值,会很敏感,可能取决于编译器实现的细节,容易导致意外的反序列化失败InvalidClassExceptions
。
7、因此,为了保证不同java编译器实现的serialVersionUID
值一致,可序列化的类必须显式声明serialVersionUID
值。显式声明的serialVersionUID
必须是static
、final
、long
类型的,这样的serialVersionUID
只作用于当前类,不会因为继承传播给其子类,也建议serialVersionUID
用private
修饰,屏蔽外界的访问。serialVersionUID
一般可以通过idea快捷键自动生成。
8、实现Serializable
的类里如果某些变量不想被序列化,可以声明成static
的,如果不能声明成静态的,也可用 transient
修饰。static
和 transient
的作用还是有差别的:
static
修饰的变量,是属于类的,自然不会被序列化和反序列化。transient
修饰的非静态变量,不会被序列化,反序列化获取到transient
修饰的变量的值是零值,所谓零值就是 0 或者 null。
9、其他序列化框架:
- json序列化框架,Jackson、FastJson、Gson
- jute序列化,看了zk源码发现,其序列化底层实现还是用的jdk的
ObjectOutputStream
和ObjectInputStream
。 - Protobuf序列化,Protobuf使用比较广泛,主要是空间开销小和性能比较好,非常适合用于公司内部对性能要求高的RPC调用。protobuf有个缺点就是要传输的每一个类的结构都要生成对应的proto文件,如果某个类发生修改,还得重新生成该类对应的proto文件。
- Thrift
- Hessian序列化,Hessian是一个支持跨语言传输的二进制序列化协议,Dubbo采用的就是Hessian序列化来实现,只不过Dubbo对Hessian进行了重构,性能更高。
- Avro序列化,Avro是apache下hadoop的子项目,拥有序列化、反序列化、RPC功能。
- kyro序列化,Kryo是一种非常成熟的序列化实现,已经在Hive、Storm中使用得比较广泛,不过它不能跨语言. 目前dubbo已经在2.6版本支持kyro的序列化机制。
finalize
finalize
子类也可以重写,默认Object里是个空方法什么也不做。建议是不要随便重写finalize
方法,因为finalize
它使得对象的回收变得不确定性。
任何一个对象的finalize
方法都只会被系统自动调用一次。什么时候调用呢?
在垃圾回收,收集器遍历GC Roots 进行第一次标记时判断一个对象的finalize
是否有必要调用:
- 如果对象没有重写
finalize
方法或者被调用过,那就没必要再调用; - 如果对象重写了
finalize
方法且没有被调用过,那么该对象将会被放置到一个队列中(F-Queue
),并在稍后由一条由虚拟机自动建立的、低调度优先级Finalizer线程
去执行它们的finalize()
方法。
finalize()方法是对象逃脱死亡命运的最后一次机会,稍后收集器将对F-Queue中的对象进行第二次小规模的标记:
- 如果对象要在finalize()中成功拯救自己,只要重新与引用链上的任何一个对象建立关联即可,譬如把自己(this关键字)赋值给某个类变量或者对象的成员变量,那在第二次标记时它将被移出“即将回收”的集合。
- 如果对象这时候还没有逃脱,那基本上就真的要被回收了。
总结
很小的一个知识点,就可以扩展开很多,把学到的知识串起来,体系化,才是真正的高手,不仅仅是为了面试,这些对实际开发也有很大帮助。具体细节深入,还需要日常积累和实践。
以上是关于面试官:Object里都有哪些方法?我:hashCodeequalstoStringwaitnotifyclonefinalize。面试官:今天的面试就到这儿了~的主要内容,如果未能解决你的问题,请参考以下文章
面试官:Object里都有哪些方法?我:hashCodeequalstoStringwaitnotifyclonefinalize。面试官:今天的面试就到这儿了~
面试官:Object里都有哪些方法?我:hashCodeequalstoStringwaitnotifyclonefinalize。面试官:今天的面试就到这儿了~