JavaSE——常用类-Object类

Object类

--所有类的父类

由于所有类都继承着Object类,因此省略了extend Object关键字。

该类中主要有以下方法:
toString()
getClass()
equals()
clone()
finalize(
其中toString(),getClass(),equals是其中最重要的方法。

image

注意: Object类中的getClass(),notify(),notifyAll(),wait()等方法被定义为final类型,因此不能重

1、clone()

详解文章:https://blog.csdn.net/zhangjg_blog/article/details/18369201#0-qzone-1-28144-d020d2
d2a4e8d1a374a433f596ad1440

所谓的复制对象,首先要分配一个和源对象同样大小的空间,在这个空间中创建一个新的对象。那么在java语言中,有几种方式可以创建对象呢?

  • 使用new操作符创建一个对象
  • 使用clone方法复制一个对象

clone在第一步是和new相似的, 都是分配内存,调用clone方法时,分配的内存和源对象(即调用clone方法的对象)相同,然后再使用原对象中对应的各个域,填充新对象的域, 填充完成之后,clone方法返回,一个新的相同的对象被创建,同样可以把这个新对象的引用发布到外部。

clone与copy的区别

假设现在有一个Employee对象,Employee tobby =new Employee(“CMTobby”,5000)

有这样的赋值Employee cindyelf=tobby,这个时候只是简单了copy了一下 。copy只是简单的拷贝,tobby和cindyelf指向内存中同一个object ,这样cindyelf或者tobby的一个操作都可能影响到对方。

这是我们不想看到的,这时使用clone就可以得到互不影响的两个对象

Employee cindy=tobby.clone(),这时会生成一个新的Employee对象,并且和tobby具有相同的属性值和方法。

1、构建Person类

package com.kuang.oop;

public class Person implements Cloneable {//续写接口,表明允许克隆
    private String name;
    private int age;

    public Person(int age, String name) {
        this.age = age;
        this.name = name;

    }

    public Person() {
    }

    public int getAge() {
        return age;

    }

    public String getName() {
        return name;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        // TODO Auto-generated method stub
        return (Person) super.clone();//必要
    }
}

2、对比clone与copy

package com.kuang.oop;


public class Test {
    public static void main(String[] args) throws CloneNotSupportedException //必要
    {
        Person p = new Person(23,"zhang");
        // Person p1 = (Person) p.clone();//clone,打印对象不同,但是必须”强转“,同时让主函数声明抛出异常才能正常运行
        Person p1 =  p;//copy,打印对象相同
        System.out.println(p);
        System.out.println(p1);
        

    }
}

浅拷贝与深拷贝

主要是JAVA里除了8种基本类型传参数是值传递,其他的类对象传参数都是引用,我们有时候不希望在方法里将参数改变,这是就需要在类中复写clone方法(实现深复制)。

浅克隆:原对象和克隆对象引用地址不同,但对象内的引用成员引用地址相同

浅拷贝存在的问题:

  • 当我们修改克隆对象teacher2的course属性的时候,teacher1的course属性也被修改了。可以推断出teacher2和teacher1的引用属性course具有相同的地址,即是同一个course对象。
  • 由此我们可以推断,调用clone方法产生的效果是:在内存中开辟一块和原始对象一样的空间,然后拷贝原始对象中的内容。但是对于基本数据类型,这样的操作是没有问题的,但对于非基本类型,它们保存的仅仅是对象的引用,这就是为什么clone后的非基本类型变量和原始对象中相应的变量会指向的是同一个对象。
    原文链接:https://blog.csdn.net/qaws9403/article/details/118089151

**深克隆:原对象和克隆对象引用地址不同,且对象内的引用成员引用地址也不同 **https://www.cnblogs.com/bleu/p/16560906.html

深克隆有两种实现方法。实现Cloneable接口和利用序列化和反序列化(简单方便)

进行深度克隆后,对clone产生的teacher2对象的course属性进行修改时,并未影响到原对象teacher1的course属性。

基本类型包括Integer,Long等对应的引用类型和String类型可以自动实现深度克隆;StringBuffer不能实现深度克隆(StringBuffer没有重写clone()方法,并且StringBuffer还是一个final类,这也是说我们也不能用继承的办法间接实现StringBuffer的clone),如果一个类中包括该类型的属性,要么只能实现浅clone,要么自己重新生成对象: new StringBuffer(oldValue.toString()); 进行赋值。

image

clone方法的保护机制

在Object中Clone()是被声明为protected的,这样做是有一定的道理的,以Employee类为例,通过声明
为protected,就可以保证只有Employee类里面才能“克隆”Employee对象

clone的使用

调用Clone()方法的对象所属的类(Class)必须implements Clonable接口,否则在调用Clone方法的时候
会抛出CloneNotSupportedException

2、toString()

public String toString() {
	return getClass().getName() + "@" + Integer.toHexString(hashCode());
}

返回一个字符串,该字符串由类名(对象是该类的一个实例)、at 标记符“@”
和此对象哈希码的无符号十六进制表示组成 。

该方法用得比较多,一般子类都有覆盖

我们推荐在学习阶段所有有属性的类都加上toString() 方法!

package com.kuang.oop;


public class Test {
    public static void main(String[] args)  {
        Object o1 = new Object();
        System.out.println(o1.toString());//java.lang.Object@2a139a55
        
    }
}

3、getClass()方法

返回Object的运行时类类型。
不可重写,要调用的话,一般和getName()联合使用,如getClass().getName();

package com.kuang.oop;

public class Test {
    public static void main(String[] args) {
        Object o1 = new Object();
        System.out.println(o1.getClass());// class java.lang.Object
        System.out.println(o1.getClass().getName());// java.lang.Object

    }
}

4、finalize()方法

该方法用于释放资源。因为无法确定该方法什么时候被调用,很少使用 。

Java允许在类中定义一个名为finalize()的方法。它的工作原理是:一旦垃圾回收器准备好释放对象占用
的存储空间,将首先调用其finalize()方法。并且在下一次垃圾回收动作发生时,才会真正回收对象占用
的内存。

关于垃圾回收,有三点需要记住:
1、对象可能不被回收。只要程序没有濒临存储空间用完的那一刻,对象占用的空间就总也得不
到释放。
2、垃圾回收并不等于“析构”。
【科普:析构函数(destructor) 与构造函数相反,当对象结束其生命周期,如对象所在的函数已调用完
毕时,系统自动执行析构函数。析构函数往往用来做“清理善后” 的工作(例如在建立对象时用new开辟
了一片内存空间,delete会自动调用析构函数后释放内存)。】
3、垃圾回收只与内存有关。使用垃圾回收的唯一原因是为了回收程序不再使用的内存。

finalize()的用途
无论对象是如何创建的,垃圾回收器都会负责释放对象占据的所有内存。
这就将对finalize()的需求限制到一种特殊情况,即通过某种创建对象方式以外的方式为对象分配了存储
空间。 即使用“本地方法”

5、equals()方法

Object中的equals方法是直接判断this和obj本身的值是否相等,即用来判断调用equals的对象和形参
obj所引用的对象是否是同一对象, 如果this和obj指向的hi同一块内存对象,则返回true,如果
this和obj指向的不是同一块内存,则返回false。

注意:即便是内容完全相等的两块不同的内存对象,也返回false。

String类已经重写了object中的equals方法(比较字符串内容是否相等)

6、hashCode()方法

返回该对象的哈希码值。
该方法用于哈希查找,可以减少在查找中使用equals的次数,重写了equals方法一般都要重写
hashCode方法。这个方法在一些具有哈希功能的Collection中用到。
一般必须满足obj1.equals(obj2)==true。可以推出obj1.hashCode() == obj2.hashCode(),但是
hashCode相等不一定就满足equals。不过为了提高效率,应该尽量使上面两个条件接近等价。

7 wait()方法

image

wait方法就是使当前线程等待该对象的锁,当前线程必须是该对象的拥有者,也就是具有该对象的锁。
wait()方法一直等待,直到获得锁或者被中断。wait(long timeout)设定一个超时间隔,如果在规定时间内没有获得锁就返回。

调用该方法后当前线程进入睡眠状态,直到以下事件发生。
(1)其他线程调用了该对象的notify方法。
(2)其他线程调用了该对象的notifyAll方法。
(3)其他线程调用了interrupt中断该线程。
(4)时间间隔到了。

此时该线程就可以被调度了,如果是被中断的话就抛出一个InterruptedException异常 。

8 notify()方法

notify()方法唤醒在该对象上等待的某个线程。

notifyAll() 方法唤醒在该对象上等待的所有线程。

posted @ 2022-08-08 16:36  群青Bleu  阅读(21)  评论(0编辑  收藏  举报