`
yidongkaifa
  • 浏览: 4120906 次
文章分类
社区版块
存档分类
最新评论

重新编写Object类中的方法

 
阅读更多

Object类是所有类的超类,也就是说,Java中的每一个类都是由Object扩展而来的。因而每当你创建一个对象,它都将拥有Object类中的全部方法。让我们先来看看java.lang.Object的中的主要方法有哪些:

public class Object{
	//公共构造函数
	public Object();
	//公共实例方法
	public boolean equals(Object obj);
	public native int hashCode();
	public final native Class getClass();
	public String toString();
	public final native void notify();
	public final native void notifyAll();
	public final void wait() throws InterruptedException;
	public final native void wait(long timeout) throws InterruptedException; 
	public final void wait(long timeout, int nanos) throws InterruptedException; 
	//保护实例方法
	protected native Object clone();
	protected void finalize() throws Throwable; 
} 


其中,方法equals测试的是两个对象是否相等,方法clone进行对象拷贝,方法getClass返回和当前对象相关的Class对象,方法notify,notifyall,wait都是用来对给定对象进行线程同步的。

对于"=="方法与equals()方法的区别中,我们首先来看一下Object类中的这个两个方法是如何定义的:

public boolean equals(Object obj) {
	return (this == obj);
    }


不难看出,它仅仅判断了两个对象的引用是否相等,如果引用指向同一内存对象,则为true,这在很多情况下并没有太大的实际意义。

所以,在Object类中equals()方法与"=="是等效的。

java语言规范要求equals方法具有以下性质:

自反性:对于任何非空引用x, x.equals(x)返回true。
对称性:对于任何非空引用x和y, 当且仅当y.equals(x)返回true时,x.equals(y)返回true。
传递性:对于任何引用x,y和z,如果x.equals(y)返回true并且y.equals(z)也返回true,那么x.equals(z)应该返回true。
一致性:如果x和y引用的对象没有改变,那么x.equals(y)的重复调用应该返回同一结果。
对于任何非空引用x, x.equals(null)应该返回false。

这5条规则显示良好的逻辑性,想要写好自己的equals方法,必须满足它们。那么如何才能做到这些规则呢?

假设当前equals方法传进来的参数名为otherObject,

测试this是否与otherObject相等。
测试otherObject是否为null,如果是,一定要返回false。
测试this通otherObject是否同属于一个类。这是为了满足规则2。
把 otherObject强制转换为另一个变量(这里称为other):Classname other = (Classname) otherObject。然后比较所有的字段。使用 == 比较基本类型字段,使用equals方法比较对象字段。如果所有的字段都匹配返回true,否则返回false。
下面举一个简单的例子:

class TestEqual{
int number;
String s;
public boolean equals(Object otherObject){
//首先看看这两个对象引用是否相等
if (this == otherObject)
return true;
//这是为了满足规则5
if(otherObject == null)
return false;
//如果两个对象所属类型不同,它们不可能相等
if(getClass() != otherObject.getClass())
return false;
TestEqual other = (TestEqual)otherObject;
//比较所有字段
return s.equals(other.s) && number == other.number;
}
}

这个例子虽然很简单,但却清晰地反映出了以上所说的要点。

还有一个需要注意的地方是,在子类中,首先要调用超类的equals方法,如果这项测试无法通过,那么两个对象不可能相等。也就是:
例如

public class TestEqualSub extends TestEqual{

public boolean equals(Object otherObject)
{
if (!super.equals(otherObject)) return false;

// super.equals checked that this and otherObject belong to the same class
TestEqualSub other = (TestEqualSub) otherObject;
return s.equals(other.s) && number == other.number;
}
}


到这里相信大家已经对编写自己的equals方法有了一定的认识,只要在平时写程序的时候多注意一下,写好它并不难。

再来看看本文的第二大主题:



如何编写自己的clone方法
类 似equals方法,我们先来讨论一下Object的clone方法效果是怎样的。不过在此之前,有个问题要提一下,所有使用clone方法的类,不论是 继承Object.clone()还是覆盖它,都必须实现一个名为cloneable的接口。如果你打开java类库查一下,会发现这个接口里什么也没 有,它仅仅是表明某个类具有被clone的能力。

用一个例子来研究一下Object.clone()的复制效果:

public class TestClone1 implements Cloneable{
int count;
TestClone1 next;

public TestClone1(int count) {
this.count=count;
if(count>0)
next=new TestClone1(count-1);
}

void add(){
count++;
if(next!=null)
next.count++;
}

public String toString(){
String s=String.valueOf(count)+" ";
if(next!=null)
s+=next.toString();
return s;
}

public Object clone(){
Object o=null;
//如果没有实现cloneable,将会抛出CloneNotSupported异常
try{
o=super.clone();
}
catch(CloneNotSupportedException e){
System.err.println("cannot clone");
}
return o;
}

public static void main(String[] args){
TestClone1 t=new TestClone1(1);
System.out.println("t="+t);
TestClone1 t1=(TestClone1)t.clone();
System.out.println("t1="+t1);
t.add();
System.out.println("after added\nt t="+t+"\nt1="+t1)
}
}


在这个例子中创建t相当于两个相连的TestClone1实例,而在调用了t的add方法之后,意想不到的结果出现了:
t=1 0
t1=1 0
after added
t t=2 1
t1=1 1
t1 也发生了改变,这点也许有些出乎你的意料。实际上Object.clone()进行的复制有着"bitwise"原则,也就是逐位复制。对于一个对象中定 义的对象,它只是简单的复制这个对象的引用。这也就是常说的浅层拷贝(shallow copy),明白了这一点,想要执行深层拷贝(deep copy)也就不难了。

只需要在TestClone1 t1=(TestClone1)t.clone();后面加上t1.next=(TestClone1)t.next.clone();就能得到:

t=1 0
t1=1 0
after added
t t=2 1
t1=1 0
这个正确的结果。

接下来要介绍的是,如何控制clone能力。从前面可以知道,clone()是protected的,你必须覆盖并实现cloneable接口并处理必要的异常才能使用它。这样,你在设计自己的类时,有以下几种风格来控制clone能力:

不理会clone(),也就是在你的类中不涉及任何有关clone()的内容,如果有人想实现clone功能,必须写一个子类并完成刚才所说的要求。这适合于Object.clone()可以胜任复制你的类中全部字段的情况。
支持clone()。你在你的类中实现cloneable接口并且覆盖clone方法以及捕获相应的异常。
有 条件的支持clone()。这种情况有些特殊,也就是你的类中保存着一些可能不能被复制的对象的引用(这里是指它们没有实现cloneable)。这时候 你只是在clone()中复制所有这些对象,如果遇到异常,抛出所有这些异常给使用你的类的人。举个例子,假设你在写一个类似于ArrayList的类, 你不知道别人会在把什么样的对象保存在你的ArrayList里,所以你也不知道他们是否能够被复制。
不实现cloneable接口但覆盖clone方法为protected。这样你虽然让你的类不能被复制,但却提供了正确的clone方法,这样如果有人想复制你的类,写一个子类实现cloneable接口就行了。
不实现cloneable接口并覆盖clone方法直接抛出异常来防止你的类被复制。不过这并不是根本的解决方法,只能对付那些直接调用你的clone方法或是在子类中调用super.clone()的人。
使用final修饰你的类来防止你的类被复制。如果你的超类中有覆盖clone方法的,再重新覆盖clone方法并直接抛出异常。这也是最彻底的办法。

分享到:
评论

相关推荐

    JAVA之Object类所有方法

    了解并正确使用`Object`类的方法对于编写健壮、高效的Java程序至关重要。在设计和实现类时,根据需要重写`equals()`、`hashCode()`和`toString()`方法,以满足特定的业务需求。同时,理解线程同步方法`wait()`、`...

    Java Object类认识

    本文将深入探讨`Object`类,以及其核心方法`equals()`与Java中的`==`操作符之间的差异。 `Object`类位于Java的核心库`java.lang`包中,它提供了基本的方法来支持对象的创建、比较和字符串表示。以下是`Object`类的...

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

    这意味着所有Java类都自动继承了`Object`类中的方法和属性。 #### 关键方法 - `public String toString()`: 返回该对象的字符串表示。默认情况下,此方法返回的是对象的类名和哈希码的十六进制表示。然而,在实际...

    day12_Object类、常用API.pdf

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

    Java面向对象(进阶)- Object类的详细概述

    通过深入理解Object类及其方法,开发者能更好地掌握Java面向对象编程的基础,编写出更健壮、易于维护的代码。在实际项目中,合理地重写`equals()`和`toString()`方法对于调试和日志记录尤其有用。

    JavaSE之Object与Object类

    - `toString()`方法是`Object`类中的一个关键方法,它的作用是返回对象的字符串表示。默认情况下,`toString()`方法返回的是类名加`@`后跟对象在内存中的哈希码的16进制表示。例如,`Person@3f99bd52`。 - 在实际...

    接口与Object类

    在深入探讨“接口与Object类”这一主题之前,我们首先明确两个核心概念——接口(interface)与抽象类(abstract class),以及它们与Java基础类库中不可或缺的Object类之间的关系。 ### 接口与抽象类 #### 接口...

    用C# MapObject编写的一个GIS程序

    在“用C# MapObject编写的一个GIS程序”中,开发者已经实现了以下几个关键功能: 1. **添加Shp文件**:程序能够读取并加载Shp文件,将其中的地理数据渲染到地图上。这涉及到对MapObject库的API调用,如`IMapServer....

    java中object类实例分析

    在Java中,hashCode方法是Object类中的一个重要方法,它是所有Java类的父类。 toString方法是返回对象的字符串表示的方法。它可以将对象的属性转换为字符串,以便于程序内查看对象属性和调试代码。 在Java中,...

    编写一个JAVA程序,定义一个Student类

    编辑一个JAVA程序,定义一个学生的类Student。这个类的属性(成员变量):“学号”,“班号”,“姓名”,“性别”,...编写一个Java Application程序,创建Student类的对象,调用上述方法进行验证并实现相应输出。

    Object Pascal中文手册(清晰非扫描版)

    尽管Object Pascal与Borland开发工具有着紧密的联系,但开发者在编写Object Pascal代码时,应当认识到某些Borland特定的规则可能与Object Pascal的通用规则有所不同。例如,Borland开发工具可能会遵循特定的文件命名...

    浅析C# 中object sender与EventArgs e

    在深入了解C#中的`object sender`与`EventArgs e`之前,我们首先需要理解.NET框架中与事件相关的类和委托的基础概念。 在C#中,事件是一种特殊的委托类型,用于表示一个对象上的特定行为。这些行为可以是用户操作,...

    PageObject.rar

    在自动化测试脚本编写时,PageObject模式鼓励将网页的交互逻辑和页面元素封装到独立的类中,从而让测试脚本更加清晰、简洁。 PageObject类通常代表一个网页或者网页的一部分,它包含了该网页的所有操作方法,如点击...

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

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

    object-c编写的计算器

    例如,`@interface`和`@implementation`关键字用于定义类的结构和实现,`#import`用于引入其他文件,以及`-(void)方法名:`用于定义方法。 计算器的核心功能包括加法、减法、乘法和除法。这些操作可以通过定义一系列...

    Java Object实例代码

    `Object`类提供了处理对象的基本方法,如`toString()`(返回对象的字符串表示),`hashCode()`(返回对象的哈希值)和`equals()`(比较两个对象是否相等)。 2. **继承和多态**: `Object`类是所有类的父类,因此...

    Object pascal中文参考手册

    1. **类与对象**:类是Object Pascal中的蓝图,定义了一组属性(Fields)和方法(Methods)。对象则是类的实例,它们具有类定义的属性和行为。创建对象时,我们使用`new`关键字,例如`var obj := TMyClass.Create;`...

    详谈Java中Object类中的方法以及finalize函数作用

    Object类中包含了一些基础且重要的方法,这些方法在Java编程中扮演着关键角色。接下来我们将详细讨论这些方法以及`finalize()`函数的作用。 1. `clone()`方法:这是一个保护方法,用于实现对象的浅复制。如果一个类...

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

    本章将深入探讨Object类的一些核心方法,如toString()和equals(),以及日期类和StringBuilder类的使用,同时涵盖包装类的概念和自动装箱、拆箱。 1. Object类 - **概述**:Object类位于java.lang包下,它是所有...

    Object pascal中文参考手册(可打印版)

    接口在Object Pascal中扮演着重要角色,它们定义了一组纯虚方法,不包含任何实现。类可以实现一个或多个接口,以声明它们支持特定的协议或行为。接口的使用有助于实现多继承的效果,并且在设计模式中起着关键作用。 ...

Global site tag (gtag.js) - Google Analytics