Object类常用方法

#clone

Java中要想自定义类的对象可以被复制,自定义类就必须实现Cloneable中的clone()方法,如下:

public class Person implements Cloneable {
    private String name;
    private Animal pet;

    public Person(String name) {
        this.name = name;
    }

    public String getName() {
        return this.name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Animal getPet() {
        return this.pet;
    }

    public void setPet(Animal pet) {
        this.pet = pet;
    }

    @Override
    public Object clone() throws CloneNotSupportedException {
        return super.clone();
    }

    public String toString() {
        return "Person[ name: " + name + ", pet: " + pet.toString() + "]";
    }
}
public class Animal {
    private String name;

    public Animal(String name) {
        this.name = name;
    }

    public String getName() {
        return this.name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "Anmial[ name: " + name + "]";
    }
}

public class Test {

    public static void main(String[] args) {
        Person ming = new Person("小明");
        Animal kitty = new Animal("喵");
        ming.setPet(kitty);
        System.out.println(ming.toString());
        try {
            Person hong = (Person) ming.clone(); // 复制ming
            hong.setName("小红");
            hong.getPet().setName("汪");
            System.out.println(hong.toString());
            System.out.println(ming.toString());
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
    }
}

结果:


结果发现,hong的名字可以改,不会影响ming,但是,改了hong的宠物的名字后,ming的宠物的名字也随之变了。
为什么?

  • 浅拷贝:

调用Object类中clone()方法产生的效果是:先在内存中开辟一块和原始对象一样的空间,然后原样拷贝原始对象中的内 容。对基本数据类型,这样的操作是没有问题的,但对非基本类型变量,我们知道它们保存的仅仅是对象的引用(引用指向堆内存中同一个对象),这也导致clone后的非基本类型变量和原始对象中相应的变量指向的是同一个对象。因此,minghong指向的实际是同一个Animal类对象。

这就是浅拷贝。

那么怎么能使得拷贝得到的原始对象中的非基本类型变量也是一个新的对象(内存地址不同,内容相同)呢?这就要深拷贝。

  • 深拷贝:

上面的例子要实现深拷贝需要完成两点:

  1. Animal类也要实现clone方法;
  2. Person类的clone方法里,拷贝一个克隆人后,还要调用Animalclone(),拷贝当前Person的宠物,然后将克隆宠物赋给克隆人的宠物,然后再返回这个真正的克隆人。

代码:

public class Person implements Cloneable {
    private String name;
    private Animal pet;

    public Person(String name) {
        this.name = name;
    }

    public String getName() {
        return this.name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Animal getPet() {
        return this.pet;
    }

    public void setPet(Animal pet) {
        this.pet = pet;
    }

    @Override
    public Object clone() throws CloneNotSupportedException {
        Person clonePerson = (Person) super.clone();
        clonePerson.pet = (Animal) pet.clone();
        return clonePerson;
    }

    public String toString() {
        return "Person[ name: " + name + ", pet: " + pet.toString() + "]";
    }
}
public class Animal implements Cloneable{
    private String name;

    public Animal(String name) {
        this.name = name;
    }

    public String getName() {
        return this.name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public Object clone() throws CloneNotSupportedException {
        return super.clone();
    }

    public String toString() {
        return "Anmial[ name: " + name + "]";
    }
}
public class Test {

    public static void main(String[] args) {
        Person ming = new Person("小明");
        Animal kitty = new Animal("喵");
        ming.setPet(kitty);
        System.out.println(ming.toString());
        try {
            Person hong = (Person) ming.clone(); // 复制ming
            hong.setName("小红");
            hong.getPet().setName("汪");
            System.out.println(hong.toString());
            System.out.println(ming.toString());
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
    }
}

运行结果:


#equals & == & hashCode

- euqls & hashCode

equals 方法是基类 Object 的方法,我们创建的所有的对象都拥有这个方法,并有权利去重写这个方法。该方法返回一个 boolean 值,代表比较的两个对象是否相同
判断相等,还有一个方法是使用==equals==有什么区别呢?其实它们两个都是通过比较内存地址(准确的说是堆内存地址)来比较两个数据是否相等,但是为什么实际使用时好像不一样呢,这是因为equals很重要的一个特点,可以被重写
先举一个字符串的例子:

public class Test {
    
    public static void main(String[] args) {
        String str1 = "123";
        String str2 = "123";
        String str3 = new String("123");
        String str4 = new String("123");
        System.out.println("str1==str2: " + (str1 == str2));
        System.out.println("str3==str4: " + (str3 == str4));
        System.out.println("str1==str3: " + (str1 == str3));
        System.out.println("str1.equals(str2): " + str1.equals(str2));
        System.out.println("str3.equals(str4): " + str3.equals(str4));
        System.out.println("str1.equals(str3): " + str1.equals(str3));
    }
}

运行结果:


使用==的三组字符串比较结果为什么是不同的?
前面说到,==比较的是数据的内存地址,使用用new()来创建对象时,对象会存放在堆中,每调用一次就会创建一个新的对象,因此堆内存地址不同;而第二种形如String str = "abc",是先在栈中创建一个对String类的对象引用变量str,然后查找堆中有没有存放String对象"abc",如果没有,则将"abc"存放进堆,并令str 指向"abc",如果已经有"abc",则直接令str 指向"abc"。因此str1str2指向同一个值为"123"的String对象,而str3str4则分别指向指向两个值为"123"的String对象,地址不同,str1str3自然也不同。和字符串一样,也有这两种创建方式的还有,上一节提到的基本数据类型的包装类型。

equals的比较结果为什么都是true呢?因为Java String类的equals方法重写了,它比较的两个字符串的内容是否相等,符合大多数时候人们对于字符串比较的需求。Java中equals方法重写了的还有Integer, Data等等。我们也可以根据自己的需求重写当前类的equals方法。

- hashCode

  • hash 法简介

hash 算法,又被成为散列算法,基本上,哈希算法就是将对象本身的键值,通过特定的数学函数运算或者使用其他方法,转化成相应的数据存储地址的。

在常见的 hash 函数中有一种最简单的方法叫「除留余数法」,操作方法就是将要存入数据除以某个常数后,使用余数作为索引值。

例如:
将 323 ,458 ,25 ,340 ,28 ,969, 77 使用「除留余数法」存储在长度为11的数组中。我们假设上边说的某个常数即为数组长度11。每个数除以11后的余数即为该数在数组中的位置。
则现在想要拿到 77 ,只需要 访问arr[77%11] 就可以了。

  • hashCode 方法

Java 中的有所的对象都拥有的hashCode 方法其实就是一种 hash 算法,只是有的类覆写好提供给我们了,有些就需要我们手动去覆写。

  • hashCode 方法的作用和意义

在 Java 中 hashCode 的存在主要是用于提高容器查找和存储的快捷性,如 HashSetHashtableHashMap 等,hashCode是用来在散列存储结构中确定对象的存储地址的。

当容器中数据很多时,通过hashCode可以快速查找数据(如上面所说的除留余数法),存储数据时也可快速查看要存储的数据是否已经存在(查重)。

要注意的是,重复的数据hashCode一定相等,但是hashCode相等的数据不一定相同。还以上面的除留余数法相同,,77除以11都余0,但是,除以11余0的不一定是77。碰撞的概率与具体的算法有关。而hashset在查重的时候则是先用hashcode来缩小寻找范围,最后还要用equals()来确定是否真的为相同的对象。

#getClass

java有两个获得类型类的方法:getClass()class()。然后再调用该类的方法可以获取该类的相关信息,比如父类的类型类getSuperclass(),该类的名字getName()

这两个方法涉及到了java中的反射和类型类。

  • 反射

反射,可以理解为在运行时期获取对象类型信息的操作。

  • 类型类

类型类指的是代表一个类型的类,因为一切皆是对象,类型也不例外,在Java使用类型类来表示一个类型。所有的类型类都是Class类的实例。

getClass()class()最直接的区别就是,getClass() 是一个类的实例所具备的方法,而class() 方法是一个类的方法。
另外getClass() 是在运行时才确定的 (反射) ,而class() 方法是在编译时就确定了。

public class Test {

    public static void main(String[] args) {
        Cat kitty = new Cat("喵");
        Animal doggy = new Animal("汪");
        System.out.println("kitty的类型类:" + kitty.getClass() + " ; Cat的类型类:" + Cat.class);
        System.out.println("doggy的类型类:" + doggy.getClass() + " ; Animal的类型类:" + Animal.class);
        System.out.println("kitty的类名:" + kitty.getClass().getName() + " ; Cat的类名:" + Cat.class.getName());
        System.out.println("kitty父类的类型类:" + kitty.getClass().getSuperclass());
    }
}

结果:


#toString

toString()是Object类的一个公有方法,而所有类都继承自Object类。所以所有类即使不实现toString方法,也会存在从Object类继承来的toString,也可以重写。

而八种基本数据类型没有toString()方法,只能使用相应的包装类才能使用toString()。

  • 例子:
public class Test {

    public static void main(String[] args) {
        Cat kitty = new Cat("喵");
        System.out.println(new Double(1.2).toString()); // 1.2
        System.out.println(new Integer(12).toString()); // 12
        System.out.println(new Boolean(true)); // true
        System.out.println(new Character('a').toString()); // a
        System.out.println(kitty.toString()); // Cat@14ae5a5
        System.out.println(kitty); // Cat@14ae5a5
    }
}

当通过System.out.println()时,参数为类对象。这时就会调用类对象的toString方法。

而类对象的toString方法返回的是对象的对象名+@+对象的十六进制的哈希值

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,569评论 4 363
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,499评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,271评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,087评论 0 209
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,474评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,670评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,911评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,636评论 0 202
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,397评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,607评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,093评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,418评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,074评论 3 237
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,092评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,865评论 0 196
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,726评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,627评论 2 270

推荐阅读更多精彩内容