`

Java 重写Object类的常见方法

阅读更多
引用
        当我们写一个类的时候,都会对Java.lang.Object类的一些重要方法进行重写,这些方法包含:hashCode(),toString(),equals(),finalize(),clone(),wait(),notify()/notifyAll() 这八个方法。

一 Equals()方法:
1.何时需要重写equals()
当一个类有自己特有的“逻辑相等”概念(不同于对象身份的概念)。
2.设计equals()
    [1]使用instanceof操作符检查“实参是否为正确的类型”。
    [2]对于类中的每一个“关键域”,检查实参中的域与当前对象中对应的域值。
    [2.1]对于非float和double类型的原语类型域,使用==比较;
    [2.2]对于对象引用域,递归调用equals方法;
    [2.3]对于float域,使用Float.floatToIntBits(afloat)转换为int,再使用==比较;
    [2.4]对于double域,使用Double.doubleToLongBits(adouble) 转换为long,再使用==比较;
    [2.5]对于数组域,调用Arrays.equals方法。

二 hashCode()方法:
1.当改写equals()的时候,总是要改写hashCode()
根据一个类的equals方法(改写后),两个截然不同的实例有可能在逻辑上是相等的,但是,根据Object.hashCode方法,它们仅仅是两个对象。因此,违反了“相等的对象必须具有相等的散列码”。
2.设计hashCode()
   [1]把某个非零常数值,例如17,保存在int变量result中;
   [2]对于对象中每一个关键域f(指equals方法中考虑的每一个域):
[2.1]boolean型,计算(f ? 0 : 1);
[2.2]byte,char,short型,计算(int);
[2.3]long型,计算(int) (f ^ (f>>>32));
[2.4]float型,计算Float.floatToIntBits(afloat);
[2.5]double型,计算Double.doubleToLongBits(adouble)得到一个long,再执行[2.3];
[2.6]对象引用,递归调用它的hashCode方法;
[2.7]数组域,对其中每个元素调用它的hashCode方法。
   [3]将上面计算得到的散列码保存到int变量c,然后执行 result=37*result+c;
   [4]返回result。

三 toString()方法:

toString方法我们处处都用到,是一个很重点的小知识,这里大概讲一下:
我们查阅API文档,查看java.lang.Object类的toString方法的详细描述是这样的:
toString
public String toString()
Returns a string representation of the object. In general, the toString method returns a string that "textually represents" this object. The result should be a concise but informative representation that is easy for a person to read. It is recommended that all subclasses override this method.
The toString method for class Object returns a string consisting of the name of the class of which the object is an instance, the at-sign character `@', and the unsigned hexadecimal representation of the hash code of the object. In other words, this method returns a string equal to the value of:

getClass().getName() + '@' + Integer.toHexString(hashCode())

Returns:
a string representation of the object.

我们大概翻译一下:
返回一个能够代表这个对象的字符串。一般而言,toString方法返回这个对象的“文本表达式”。这个返回的结果很简洁但是不是易于人们阅读的信息表达式。这里推荐大家在使用的子类的时候重写该方法。

对于Object这个类而言,toString方法返回值是由所属类的类名、一个“@”符号和这个对象哈希码的无符号十六进制表达式组成的。换句话说,这个方法返回的字符串等同于下面的方法返回的值:

    getClass().getName()+ '@' + Integer.toHexString(hashCode())

返回:
    这个对象的字符串表达式

我们再看看java.lang.String类中的toString方法,看看是否一样呢
toString
public String toString()
This object (which is already a string!) is itself returned.

Specified by:
toString in interface CharSequence
Overrides:
toString in class Object
Returns:
the string itself.

我们还是翻译一下吧:
这个对象自己(它已经是一个字符串了)就是返回值
说明:
    CharSequence接口中的toString方法
重写:
    重写了Object中的toString方法
返回:
    自己本身的字符串

我们可以看到,String类继承Object类,并且重写了toString方法,也就是说他有自己的toString方法,它返回一个自己本身的字符串,而自己本身就是字符串对象,这样就不会出现一推哈希码了。
在这里为了更加明确,我们从“说明”中看到,String中toString方法是CharSequence接口的toString方法,那我们看一看java.lang.CharSequence接口的toString方法的定义:
toString
String toString()
Returns a string containing the characters in this sequence in the same order as this sequence. The length of the string will be the length of this sequence.

Overrides:
toString in class Object
Returns:
a string consisting of exactly this sequence of characters

我们还是翻译一下:
返回一个字符串,该字符串包含了与这个序列顺序相同的所有字符。字符串的长度就是该序列的长度

重写:
Object的toString
返回:
由该序列的特定的字符组成的字符串

四 clone()方法:
对于克隆(Clone),Java有一些限制:
1、被克隆的类必须自己实现Cloneable 接口,以指示 Object.clone() 方法可以合法地对该类实例进行按字段复制。Cloneable 接口实际上是个标识接口,没有任何接口方法。
2、实现Cloneable接口的类应该使用公共方法重写 Object.clone(它是受保护的)。
3、在Java.lang.Object类中克隆方法是这么定义的:
protected Object clone() throws CloneNotSupportedException
创建并返回此对象的一个副本。表明是一个受保护的方法,同一个包中可见。
按照惯例,返回的对象应该通过调用 super.clone 获得。
浅拷贝:指的是你的类本身被拷贝,而没有拷贝类本身属性中的类。
深拷贝:指的是包含类本身和属性类在内的所有类的拷贝。
简单点说:就是浅拷贝的两个对象中的属性还会指向同一个类,而深拷贝则全部单独了。也就是说深拷贝把关联关系也拷贝了。可以简单理解为深拷贝中的数据类型中含有类类型的变量,因为java中传递参数的形式是以传值方式进行的。

五 finalize()方法:

protected void finalize() throws Throwable { }:众所周知,finalize()方法是GC(garbage collector)运行机制的一部分

finalize()方法是在GC清理它所从属的对象时被调用的,如果执行它的过程中抛出了无法捕获的异常(uncaught exception),GC将终止对改对象的清理,并且该异常会被忽略;直到下一次GC开始清理这个对象时,它的finalize()会被再次调用。

请看下面的示例:
Java代码
public final class FinallyTest {
// 重写finalize()方法
protected void finalize() throws Throwable {
System.out.println("执行了finalize()方法");
}
public static void main(String[] args) {
FinallyTest ft = new FinallyTest();
ft = null;
System.gc();
}
}

运行结果如下:
• 执行了finalize()方法
程序调用了java.lang.System类的gc()方法,引起GC的执行,GC在清理ft对象时调用了它的finalize()方法,因此才有了上面的输出结果。调用System.gc()等同于调用下面这行代码:
Java代码
Runtime.getRuntime().gc();
调用它们的作用只是建议垃圾收集器(GC)启动,清理无用的对象释放内存空间,但是GC的启动并不是一定的,这由JAVA虚拟机来决定。直到 JAVA虚拟机停止运行,有些对象的finalize()可能都没有被运行过,那么怎样保证所有对象的这个方法在JAVA虚拟机停止运行之前一定被调用呢?答案是我们可以调用System类的另一个方法:
Java代码
public static void runFinalizersOnExit(boolean value) {
//other code
}

给这个方法传入true就可以保证对象的finalize()方法在JAVA虚拟机停止运行前一定被运行了,不过遗憾的是这个方法是不安全的,它会导致有用的对象finalize()被误调用,因此已经不被赞成使用了。
由于finalize()属于Object类,因此所有类都有这个方法,Object的任意子类都可以重写(override)该方法,在其中释放系统资源或者做其它的清理工作,如关闭输入输出流。

六 wait()/notify()/notifyAll()

通常,多线程之间需要协调工作。例如,浏览器的一个显示图片的线程displayThread想要执行显示图片的任务,必须等待下载线程 downloadThread将该图片下载完毕。如果图片还没有下载完,displayThread可以暂停,当downloadThread完成了任务后,再通知displayThread“图片准备完毕,可以显示了”,这时,displayThread继续执行。

  以上逻辑简单的说就是:如果条件不满足,则等待。当条件满足时,等待该条件的线程将被唤醒。在Java中,这个机制的实现依赖于wait/notify.等待机制与锁机制是密切关联的。例如:

synchronized(obj) {
while(!condition) {
obj.wait();
}
obj.doSomething();
} 


  当线程A获得了obj锁后,发现条件condition不满足,无法继续下一处理,于是线程A就wait()。在另一线程B中,如果B更改了某些条件,使得线程A的condition条件满足了,就可以唤醒线程A:

synchronized(obj) {
condition = true;
obj.notify();
}


  需要注意的概念是:

  ◆调用obj的wait(), notify()方法前,必须获得obj锁,也就是必须写在synchronized(obj) {……} 代码段内。

  ◆调用obj.wait()后,线程A就释放了obj的锁,否则线程B无法获得obj锁,也就无法在synchronized(obj) {……} 代码段内唤醒A。     ◆当obj.wait()方法返回后,线程A需要再次获得obj锁,才能继续执行。

  ◆如果A1,A2,A3都在obj.wait(),则B调用obj.notify()只能唤醒A1,A2,A3中的一个(具体哪一个由JVM决定)。

  ◆obj.notifyAll()则能全部唤醒A1,A2,A3,但是要继续执行obj.wait()的下一条语句,必须获得obj锁,因此,A1,A2,A3只有一个有机会获得锁继续执行,例如A1,其余的需要等待A1释放obj锁之后才能继续执行。

  ◆当B调用obj.notify/notifyAll的时候,B正持有obj锁,因此,A1,A2,A3虽被唤醒,但是仍无法获得obj锁。直到B退出synchronized块,释放obj锁后,A1,A2,A3中的一个才有机会获得锁继续执行。
分享到:
评论

相关推荐

    Java的Object类讲解案例代码 equals()、hashCode()、finalize()、clone()、wait()

    内容概要 这个源码资源是关于Java中的Object类的讲解案例代码。Object类是所有Java类的根类...理解equals()、hashCode()、toString()等常用方法的用途。 学会正确重写这些方法,以满足特定需求。应用实例代码中提供的场

    Object类,Objects类,Date类,DateFormat类,Calendar类,Math类,system类

    方法:toStringequals总结:二.Objects类概念三.Date类概述构造方法:成员方法:总结:四.DateFormat类概念构造方法:成员方法:使用方法演示五.Calendar类概念:方法:六.Math概念使用方法:常用方法:七.system...

    Java常见基础知识点.md

    * [面向对象和面向过程的区别](#面向对象和面向过程的区别) ... * [Object类的方法有哪些?](#object类的方法有哪些) * [静态属性方法和成员属性方法区别](#静态属性方法和成员属性方法区别) * [子类属性

    java 面对对象编程.pdf.zip

    Object 类的常见方法有哪些? == 和 equals() 的区别 hashCode() 有什么用? 为什么要有 hashCode? 为什么重写 equals() 时必须重写 hashCode() 方法? String String、StringBuffer、StringBuilder 的区别? ...

    疯狂JAVA讲义

    学生提问:既然内部类是外部类的成员,是否可以为外部类定义子类,在子类中再定义一个内部类来重写其父类中的内部类? 211 6.7.4 局部内部类 211 6.7.5 匿名内部类 212 6.7.6 闭包(Closure)和回调 215 6.8 ...

    Java基础知识点总结.docx

    Array方法类汇总 304 Java数组与集合小结 305 递归 309 对象的序列化 310 Java两种线程类:Thread和Runnable 315 Java锁小结 321 java.util.concurrent.locks包下常用的类 326 NIO(New IO) 327 volatile详解 337 ...

    2022常见JAVA面试题一.docx

    这是一个 protected 方法,它在 Object 类中被定义,因此所有的类都继承了它。 二、Anonymous Inner Class 的使用 Anonymous Inner Class 是 JAVA 中的一种内部类,它没有名称,但可以作为一个接口的实现。它不能...

    JAVA基础课程讲义

    String类的常用方法(已讲过,不再讲!) 120 StringBuffer和StringBuilder 121 String和StringBuffer和StringBuilder使用要点 123 时间处理相关类 124 Date时间类(java.util.Date) 124 DateFormat类和...

    java 面试题 总结

    wait是Object类的方法,对此对象调用wait方法导致本线程放弃对象锁,进入等待此对象的等待锁定池,只有针对此对象发出notify方法(或notifyAll)后本线程才进入对象锁定池准备获得对象锁进入运行状态。 14、Overload...

    JAVA面试题最全集

    51.Object类(或者其子类)的finalize()方法在什么情况下被调用? 52.一个“.java”原文件中是否可以包括多个类(不是内部类)? 53.掌握内部类和接口的概念 54.StringTokenizer类的使用 55.数据结构,如何遍历...

    java基础案例与开发详解案例源码全

    6.2 Object类160 6.3 多态161 6.3.1 多态概念的理解161 6.3.2 Java中多态的实现162 6.2.3 类型检测.向上转型.向下转型164 6.2.4 动态绑定166 6.4 访问修饰符167 6.5 static修饰符168 6.5.1 静态变量168 6.5.2 静态...

    AIC的Java课程1-6章

    第9章 常用类 4课时  理解Object类及其常用方法equals,hashCode和finalize等。  能够使用String,StringBuffer,StringBuilder类创建字符串对象和使用其方法,分辨不同类之间的区别。 ...

    JAVA面向对象详细资料

    23.1 常用方法:boolean equals(Object) 33 23.2 常用方法:String toString() 34 24 继承整理 34 25 面向对象三大特性:多态(核心) 34 25.1 多态的前提? 35 25.2 什么是多态? 35 25.3 父类的引用指向子类实例...

    Java 语言基础 —— 非常符合中国人习惯的Java基础教程手册

    由于对构造方法可以进行重写 ,所以通过给出不同个数或类型的参数会分别调用不同的构造方法。 例子:以类 Rectangle 为例,我们生成类 Rectangle 的对象: Rectangle p1=new Rectangle (); Rectangle p2=new Rectangle...

    21天学会Java之(Java SE第三篇):包、方法、对象和类、类的定义、属性(成员变量)、构造器(构造方法)

    文章目录包包(package)的作用JDK中主要的包包的命名规范导入类(import)方法方法的使用方法的重载(Overload)以及方法的重写(Override)对象(Object)和类(class)面向过程和面向对象对象的本质对象和类的概念类的定义...

    java培训机构内部预习文档

    chp1.语言基础 基本语法、标识符、命名规范、八种基本类型、基本操作符、运算符 chp2.选择结构 if 、if else、if else if else、switch case chp3.循环控制 while、do while、...反射 类对象及其获取方式,反射常用方法

    超级有影响力霸气的Java面试题大全文档

    wait是Object类的方法,对此对象调用wait方法导致本线程放弃对象锁,进入等待此对象的等待锁定池,只有针对此对象发出notify方法(或notifyAll)后本线程才进入对象锁定池准备获得对象锁进入运行状态。 17、...

    Java面试宝典2020修订版V1.0.1.doc

    25、JAVA中Object类中有哪些常用方法? 19 26、heap和stack有什么区别。 19 27、GC是什么? 为什么要有GC? 20 28、什么是内部类?分为哪几种? 20 29、为什么需要内部类? 20 30、内部类可以引用它的包含类的成员吗?...

Global site tag (gtag.js) - Google Analytics