`
i_am_birdman
  • 浏览: 280699 次
  • 性别: Icon_minigender_1
  • 来自: 厦门
社区版块
存档分类
最新评论

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代码
  1. Java代码  
  2. public   final   class  FinallyTest {  
  3. // 重写finalize()方法   
  4. protected   void  finalize()  throws  Throwable {  
  5. System.out.println("执行了finalize()方法" );  
  6. }  
  7. public   static   void  main(String[] args) {  
  8. FinallyTest ft = new  FinallyTest();  
  9. ft = null ;  
  10. System.gc();  
  11. }  
  12. }  
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代码

Java代码
  1. public   static   void  runFinalizersOnExit( boolean  value) {  
  2. //other code   
  3. }  
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.等待机制与锁机制是密切关联的。例如:

Java代码
  1. synchronized (obj) {  
  2. while (!condition) {  
  3. obj.wait();  
  4. }  
  5. obj.doSomething();  
  6. }   
synchronized(obj) {
while(!condition) {
obj.wait();
}
obj.doSomething();
} 



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

Java代码
  1. synchronized (obj) {  
  2. condition = true ;  
  3. obj.notify();  
  4. }  
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类常方法 Math类常用方法 System类常用方法 基本类型包装类常用方法等

    #### 一、Object类常用方法 在Java中,`Object`类是最基础的类,所有其他类都直接或间接继承自它。因此了解并掌握`Object`类的方法对于深入理解Java编程至关重要。 1. **toString() 方法**: - **用途**:提供了...

    Java Object类的常用方法详解与应用实践

    Object类的方法是Java编程的基础,它们为所有对象提供了基本的行为。掌握这些方法的用法和最佳实践,可以帮助我们编写出更加健壮、高效和可维护的Java代码。通过重写equals、hashCode和toString方法,我们可以确保...

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

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

    day01【Object类、常用API】-笔记.md

    # Java核心知识点详解:Object类与常用API ## Object类概览 ### 1.1 概述 在Java编程语言中,`java.lang.Object`类具有非常特殊的地位,它是所有Java类的基类,即使程序员没有明确地指定某个类继承自`Object`类,...

    Object类常用方法(csdn)————程序.pdf

    Object 类常用方法 Object 类是 Java 语言中的顶层类,是所有类的直接或间接父类。任何类,如果没有书写 extends 显式继承某个类,都默认直接继承 Object 类,否则为间接继承。Object 类中所定义的方法,是所有对象...

    Object类和Scanner类

    ### Object类和Scanner类 #### 一、Object类详解 ...通过以上介绍可以看出,`Object` 类和 `Scanner` 类分别代表了Java中对象的基本行为和简单的输入处理功能,它们都是Java编程不可或缺的一部分。

    Java基础知识-day01【Object类、常用API】.pdf

    【Java基础知识-day01【Object类、常用API】】 在Java编程中,了解和掌握Object类及其常用API是至关重要的,因为所有Java类都间接或直接地继承自Object类。本章将深入探讨Object类的一些核心方法,如toString()和...

    JAVA中常用类的常用方法.docx

    JAVA 中有许多常用的类,每个类都有其特定的方法,本文将对 java.lang.Object 类、String 类和 StringBuffer 类的常用方法进行讲解。 一、java.lang.Object类 java.lang.Object 类是 JAVA 中所有类的父类,它提供...

    关于Java中Object类的几个方法示例

    在Java编程中,Object类的方法非常重要,常见的如getClass()、toString()和equals()。 1. getClass()方法: 这个方法用于获取运行时对象的类类型。这个方法返回的是一个Class类型的对象,它代表了对象的类型信息。...

    java之Object类用法实例

    Java中的`Object`类是所有Java类的根类,无论你定义...对于自定义类,根据业务需求重写`equals()`方法是常见的做法,以确保比较的是对象的内容而非引用。正确理解和使用`equals()`方法对于编写健壮的Java代码至关重要。

    浅谈java 重写equals方法的种种坑

    Java 中的 equals 方法是一种用于比较对象是否相等的方法,它是 Object 类中的一个方法。然而,重写 equals 方法并不是一件简单的事情,因为它需要遵守一些约定,否则可能会出现一些不可预期的行为。在这篇文章中,...

    day12_Object类、常用API.pdf

    Object类中定义了native修饰的方法,称为本地方法,本地方法的特点是被native修饰的方法,非Java语言编写,是由C++语言编写。本地方法在运行时期进入本地方法栈内存,本地方法栈是一块独立内存的区域。本地方法的...

    Java SE编程入门教程 java object(共9页).pptx

    Java SE编程入门涉及众多基础知识,这里我们主要关注Java对象、Object类及其常用方法。Java语言是一种面向对象的编程语言,所有类都直接或间接地继承自Object类,它位于核心库`java.lang`包中。 1. **Object类**: ...

    java课件 常用类与异常处理

    在Java中,`Object`类是所有类的父类,它提供了几个核心的方法,包括`equals()`, `hashCode()`, 和 `toString()`。 `equals()`方法用于比较两个对象是否相等。默认实现比较的是对象的引用,即两个对象是否指向内存...

    Java中的面向对象编程Object-Oriented Programming in Java

    - 例如,`Car`类有一个`drive()`方法,`ElectricCar`继承自`Car`并重写了`drive()`方法,当调用`ElectricCar`对象的`drive()`时,执行的是`ElectricCar`类中的版本。 5. **抽象类与接口**: - 抽象类不能被实例化...

    Chp10 Object类与常用类,内部类.pdf

    在Java编程语言中,`Object`类是一个非常关键的类,它...总的来说,理解和掌握`Object`类及其方法、垃圾回收机制、包装类和`String`类以及内部类的使用,对于深入理解Java编程至关重要,也是初学者必须掌握的基础知识。

    Java实例化类的方法.docx

    本文介绍了Java中实例化类的四种常用方法:使用`new`操作符、调用`Class`对象的`newInstance()`方法、调用`clone()`方法以及通过`ObjectInputStream`的`readObject()`方法反序列化类。每种方法都有其适用场景和局限...

Global site tag (gtag.js) - Google Analytics