美文网首页real
Object类及常用方法简介

Object类及常用方法简介

作者: 水欣 | 来源:发表于2018-03-13 14:05 被阅读13次

    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的对象模型:


    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类及常用方法简介

        本文链接:https://www.haomeiwen.com/subject/tltnfftx.html