Object类及常用方法简介

Object类是一个特殊的类,是所有类的父类,是java中唯一没有父类的类,如果一个类没有用extends明确指出继承于某个类,那么它默认继承Object类。

1.获取对象信息的方法:toString

public class Student {

    private String name = "Mary";
    private int age = 21;

    public static void main(String[] args) {
        Student student = new Student();

        System.out.println(student.toString());
    }
    
}

输出结果

com.netty.test23.Student@24367013

toString()方法,它用于返回标识对象值的字符串。类名@内存地址
随处可见toString()的主要原因是:只要对象与一个字符串通过操作符“+”连接起来,java编译器就会自动地调用toString方法,以便获得这个对象的字符串描述。

2.equals()

判断两个对象是否相等的方法。可以先判断地址是否相等,再判断值是否相等。自定义对象需要重写该方法,不然该方法就是对比地址是否相等。

 public boolean equals(Object obj) {
        return (this == obj);
    }
  • 以上方法,很明显是对两个对象的地址值进行比较(即比较引用是否相同)。
    看下String中该方法的实现,先判断地址是否相等,再判断类型、字符长度,各个字符是否相等。
public boolean equals(Object anObject) {
        if (this == anObject) {
            return true;
        }
        if (anObject instanceof String) {
            String anotherString = (String) anObject;
            int n = value.length;
            if (n == anotherString.value.length) {
                char v1[] = value;
                char v2[] = anotherString.value;
                int i = 0;
                while (n-- != 0) {
                    if (v1[i] != v2[i])
                            return false;
                    i++;
                }
                return true;
            }
        }
        return false;
    }
  • 很明显,这是进行内容的比较,而已经不再是地址的比较。

3.hashCode()

返回当前对象的hash code value,这个类是用来支持一些hash table,例如HashMap。
它的性质是:

  • 在一个Java应用的执行期间,如果一个对象提供给equals做比较的信息没有被修改的话,该对象多次调用hashCode()方法,该方法必须始终如一返回同一个Integer
  • 如果两个对象根据equals(Object)方法是相等的,那么调用二者各自的hashCode()方法必须产生同一个integer结果。
  • 并不要求根据equals(lava.lang.Object)方法不相等的两个对象,调用二者各自的hashCode()方法必须产生不同的integer结果
    在集合查找时,hashcode能大大降低对象比较次数,提高查找效率
    Java对象的equals()和hashCode()是这样规定的:
  1. 相等(相同)的对象必须具有相等的哈希码(或者散列码)。
    解释:想象一下,如果两个java对象A和B,A和B相等(equals结果为true),但A和B的哈希码不同,则A和B存入HashMap时的哈希码计算得到的HashMap内部数组位置索引可能不同,那么A和B很有可能允许同时存入HashMap,显然相等/相同的元素是不允许同时存入HashMap,HashMap不允许存放重复元素。
  2. 如果两个对象的hashCode相同,他们并不一定相等。
    解释:也就是说,不同对象的HashCode可能相同;假如两个Java对象A和B,A和B不相等(equals结果为false),但A和B的哈希码相等,将A和B都存入HashMap时会发生哈希冲突,也就是A和B存入在HashMap时会发生哈希冲突,也就是A和B存放在HashMap内部数组的位置索引相同,这是HashMap会在该位置建立一个链表,将A和B串起来放在该位置,显然,该情况不违反HashMap的使用原则,是允许的。当然,hash冲突越少越好,尽量采用好的hash算法以避免哈希冲突。
    所以,java对于equals方法和hashCode方法是这样规定的:
1. 如果两个对象相同,那么它们的hashCode值一定要相同;
2. 如果两个对象的hashCode相同,它们并一定相同(这里说的对象相同指的是用equals方法比较)。
3. equals()相等的两个对象,hashCode()一定相等;equals()不相等的两个对象,却并不能证明他们的hashCode()不相等。
换句话说,equals()方法不相等的两个对象,hashcode()有可能相等(我的理解是由于哈希码在生成的时候产生冲突造成的)。反过来,hashcode()不等,一定能推出equals()也不等;hashcode()相等,equals()可能相等,也可能不等。

在object类中,hashcode()方法是本地方法,返回的是对象的地址值,而object类中的equals()方法比较的也是两个对象的地址值,如果equals()相等,说明两个对象地址值也相等,当然hashcode()也就相等了;在String类中,equals()返回的是两个对象内容的比较,当两个对象内容相等时,Hashcode()方法根据String类的重写代码的分析,也可知道hashcode()返回结果也会相等。以此类推,可以知道Integer、Double等封装类中经过重写的equals()和hashcode()方法也同样适合于这个原则。当然没有经过重写的类,在继承了object类的equals()和hashcode()方法后,也会遵守这个原则。

4. clone()

源码

protected native Object clone() throws CloneNotSupportedException;

又源码我们会发现:
第一:Object类的clone()方法是一个native方法,native方法的效率一般来说都是远高于java中的非native方法。这也解释了为什么要用Object中的clone方法而不是先new一个类,然后把原始对象中的信息复制到新对象中,虽然这也实现了clone功能。
第二:Object类中的clone()方法被protected修饰符修饰。这也意味着如果要应用clone()方法,必须继承Object类,在java中所有的类是缺省继承Object类的,也就不用关心这点了。然后重载clone()方法。这一点要考虑的是为了让其他类能调用这个对象类的clone()方法,重载之后要把clone()方法的属性设置为public.
第三:Object.clone()方法返回一个Object对象。我们必须进行强制类型转换才能得到我们需要的类型。

浅层复制与深层复制

浅层复制:被复制的对象的所有成员属性都与原来的对象值相同,而所有的对其他对象的引用仍然指向原来的对象。换言之,浅层复制仅仅复制所考虑的对象,而不复制它引用的对象。

  • 当某个类要复写clone方法时,要继承Cloneable接口。通常的克隆对象都是通过super.clone()方法来克隆对象。
  • 浅拷贝就是拷贝一个对象的副本。若只需要复制对象的字段值(对于基本数据类型,如:int,long,float等,则复制值;对于复合数据类型仅复制该字段值,如数组变量则复制地址,对于对象变量则复制对象reference).
public class ShadowClone implements Cloneable {

    private int a;
    private int[] b;

    public Object clone() {
        ShadowClone sc = null;

        try {
            sc = (ShadowClone) super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return sc;
    }

    public int getA() {
        return a;
    }

    public void setA(int a) {
        this.a = a;
    }

    public int[] getB() {
        return b;
    }

    public void setB(int[] b) {
        this.b = b;
    }

    public static void main(String[] args) {
        ShadowClone c1 = new ShadowClone();

        c1.setA(100);
        c1.setB(new int[]{1000});

        System.out.println("克隆前c1: a=" + c1.getA() + " b=" + c1.getB()[0]);

        ShadowClone c2 = (ShadowClone) c1.clone();
        c2.setA(50);
        int[] a = c2.getB();
        a[0] = 5;
        c2.setB(a);

        System.out.println("克隆前c1:  a=" + c1.getA() + " b=" + c1.getB()[0]);
        System.out.println("克隆后c2:  a=" + c2.getA() + " b[0]=" + c2.getB()[0]);
    }
}

结果为:

克隆前c1: a=100 b=1000
克隆前c1:  a=100 b=5
克隆后c2:  a=50 b[0]=5

c1和c2的对象模型:


Object类及常用方法简介_第1张图片
image.png
  • 可以看出,基本类型可以使用浅拷贝,而对于引用类型,由于引用的是内容相同,所以改变c2实例对象中的属性就会影响到c1。所以引用类型需要使用深拷贝。另外,在开发一个不可变类的时候,如果这个不可变类中成员有引用类型,则就需要通过深拷贝来达到不可变的目的。
  • 需要注意:
    1. 希望能实现clone功能的CloneClass类实现了Cloneable接口,这个接口属于java.lang 包,java.lang包已经被缺省的导入类中,所以不需要写成java.lang.Cloneable。
    2. 重载了clone() 方法。最后在clone()方法中调用了super.clone(),这也意味着无论clone类的继承结构是什么样的,super.clone()直接或间接调用了java.lang.Object类的clone()方法
    3. Object类的clone()是一个native方法,native方法的效率一般来说都是远高于java中的非native方法。这也解释了为什么要用Object中clone()方法而不是先new一个类,然后把原始对象中的信息赋到新对象中,虽然这也实现了clone方法。对于第二点,也要 观察Object类中的clone()还是一个protected属性的方法。这也意味着如果要应用clone()方法,必须继承Object类,在 Java中所有的类是缺省继承Object类的,也就不用关心这点了。然后重载clone()方法。还有一点要考虑的是为了让其它类能调用这个clone 类的clone()方法,重载之后要把clone()方法的属性设置为public。

深层复制:被复制对象的所有变量都含有与原来的对象相同的值,除去那些引用其他对象的变量。那么引用其他对象的变量将指向被复制过的新对象,而不是原有的那些被引用的对象。换言之,深层复制要复制的对象引用的对象都复制一遍。

public class Student implements Cloneable {
    private String name;
    private int age;
    Professor pro;
    public Student(){}
    public Student(String name,int age,Professor pro){
        this.name=name;
        this.age=age;
        this.pro=pro;
    }
    public Object clone(){
        Student o=null;
        try {
            //Object中的clone()识别出你要复制的是哪一个对象。
            o=(Student)super.clone();
        } catch (CloneNotSupportedException e) {
            System.out.println(e.toString());
        }
        o.pro=(Professor)pro.clone();
        return o;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public Professor getPro() {
        return pro;
    }
    public void setPro(Professor pro) {
        this.pro = pro;
    }
}
class Professor implements Cloneable{
    private String name;
    private int age;
    public Professor(){}
    public Professor(String name,int age){
        this.name=name;
        this.age=age;
    }
    public Object clone(){
        Object o=null;
        try {
            o=super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return o;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
}

你可能感兴趣的:(Object类及常用方法简介)