面试官:Object里都有哪些方法?我:hashCodeequalstoStringwaitnotifyclonefinalize。面试官:今天的面试就到这儿了~

Posted 徐同学呀

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了面试官:Object里都有哪些方法?我:hashCodeequalstoStringwaitnotifyclonefinalize。面试官:今天的面试就到这儿了~相关的知识,希望对你有一定的参考价值。

Object里都有哪些方法?

问题看着简单,但是考察面很广,每一个方法要扩展开来的话都可以单独成为一篇文章。

主要考察几个知识点:

  • hashCodeequals 的使用,实际开发中有没有重写过之类的。
  • waitnotify 搭配synchronized 使用,还可以扯到synchronized上,包括synchronized底层实现、锁的升级,以及与AQS的对比。
  • clone,浅克隆和深克隆,浅克隆需要注意什么?深克隆如何实现,又可以扯到jdk的序列化,以及开源的序列化方案。
  • finalize,重写 finalize 注意事项,什么时候会调用 finalize,涉及到垃圾回收器GC Roots标记过程,扯到这里就可以继续扯垃圾收集算法、垃圾回收器了,进而扯到 jvm 的调优。

hashCode、equals、toString

Object是所有类的超类,它里面常见的几个方法 hashCodeequalstoString,这几个方法也是平时在子类中最常重写的方法。

wait、notify

还有waitnotify方法,需要和 synchronized 搭配使用,可以实现一个简易的 生产者-消费者模式。

clone

还有 clonefinalize,这两个方法都是protected修饰的,不能直接调用,子类想调用这两个方法必须重写。

1、先说一下clone方法,子类重写clone方法,可以直接调用父类Objectclone,它是个native方法,它的克隆算是浅克隆,虽然克隆出来的实例对象是一个新对象,但是如果原实例对象里有引用对象是共享的,意思就是修改原对象中的引用对象中的属性值,克隆对象中的引用对象的属性值也会被修改。

2、调用父类Object 的clone还需要注意,子类必须实现一个接口 Cloneable,它就是一个空接口,里面没有方法定义,目的就是标识这个类支持克隆,如果没有实现接口Cloneable,就会抛一个 CloneNotSupportedException 异常。

3、如果想实现深克隆,就得自己实现克隆的逻辑,比如自己new一个新对象,然后把引用对象也new一下。

4、还可以通过序列化和反序列化实现深克隆,序列化和反序列化可以直接用jdk自带的 ObjectOutputStreamObjectInputStream实现,不过需要注意,序列化的类以及其类的实例对象里的引用对象对应的类都必须实现接口 Serializable ,它也是一个空接口,目的也是标识 该类 支持序列化和反序列化。

5、说到接口Serializable,就不得不说一下 serialVersionUID,序列化和反序列化的过程中,每个可序列化的类都会关联一个版本号,就是 serialVersionUID,在反序列化的时候,会验证序列化对象的发送方和接收方的serialVersionUID是否相等,相等就可以反序列化,不相等就会反序列化失败(InvalidClassException异常)。

6、如果不显式声明serialVersionUID,则序列化运行时将根据类的各个方面计算该类的默认serialVersionUID 值,类似于md5和checksum的作用。但是强烈建议显式声明serialVersionUID,因为缺省的serialVersionUID计算的默认值,会很敏感,可能取决于编译器实现的细节,容易导致意外的反序列化失败InvalidClassExceptions

7、因此,为了保证不同java编译器实现的serialVersionUID值一致,可序列化的类必须显式声明serialVersionUID值。显式声明的serialVersionUID必须是staticfinallong类型的,这样的serialVersionUID只作用于当前类,不会因为继承传播给其子类,也建议serialVersionUIDprivate修饰,屏蔽外界的访问。serialVersionUID一般可以通过idea快捷键自动生成。

8、实现Serializable的类里如果某些变量不想被序列化,可以声明成static的,如果不能声明成静态的,也可用 transient 修饰。statictransient 的作用还是有差别的:

  • static修饰的变量,是属于类的,自然不会被序列化和反序列化。
  • transient修饰的非静态变量,不会被序列化,反序列化获取到transient修饰的变量的值是零值,所谓零值就是 0 或者 null。

9、其他序列化框架:

  • json序列化框架,Jackson、FastJson、Gson
  • jute序列化,看了zk源码发现,其序列化底层实现还是用的jdk的ObjectOutputStreamObjectInputStream
  • 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。面试官:今天的面试就到这儿了~

查询oracle 数据库里都有哪些表锁死

留学美国常见面试问题都有哪些

3.redis 都有哪些数据类型?分别在哪些场景下使用比较合适?

面试突击Mysql:Mysql都有哪些特性?分别适用于哪些场景?