Object中方法的使用探究

Object类中自带方法的使用探究

我们可以通过new一个Object对象查看其方法。
在这里插入图片描述
可以看见里面有9个方法。

equals()

equals意为比较。我们查看jdk1.8中自带的src压缩包中Object类并找到equals的源码

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

默认情况下它是将两个对象进行地址的比较,如果地址相同,返回true否则返回false。
但是在很多情况下,我们自定义的类并不能直接调用Object 提供的equals方法来进行判断两个对象在实际意义上是否相同。

class Person{
	public int id;
	public String name;
	public int phoneNo;
	public Person() {
		// TODO Auto-generated constructor stub
	}
	Person(int id,String name,int phone){
		this.id=id;
		this.name=name;
		this.phoneNo=phone;
	}
	@Override
	public String toString() {
		// TODO Auto-generated method stub
		return "姓名:"+this.name+" 身份证号码"+this.id+" 手机号码"+this.phoneNo;
	}
	@Override
	public int hashCode() {
		// TODO Auto-generated method stub
		return this.name.hashCode()+this.id*31+this.phoneNo*31;
	}
	@Override
	public boolean equals(Object arg0) {
		// TODO Auto-generated method stub
		if (arg0==this) {
			return true;
		}
		if (arg0 instanceof Person) {
			Person person=(Person)arg0;
			if (person.hashCode()==this.hashCode()) {
				return true;
			}
			if (person.getId()==this.getId()&&person.getName().equals(this.getName())&&person.getPhoneNo()==this.getPhoneNo()) {
				return true;
			}
		}
		return false;
	}
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getPhoneNo() {
		return phoneNo;
	}
	public void setPhoneNo(int phoneNo) {
		this.phoneNo = phoneNo;
	}
}
public class DEmo {
	public static void main(String[] args) {
		Person p1=new Person(123456,"王邦彦",13311111);
		Person p2=p1;
		Person p3=new Person(123456,"王邦彦",13311111);
		Person p4=new Person(123456,"王邦彦",13311112);
		if (p1.equals(p2)) {
			System.out.println(1);
		}
		if (p1.equals(p3)) {
			System.out.println(3);
		}
		if (p1.equals(p4)) {
			System.out.println(4);
		}
	}
}

此时我们有4个引用对象,其中p1指向(123456,“王邦彦”,13311111)这个对象,p2只是将p1的引用地址拿了过来所以一样指向p1的对象,p3指向一个new的(123456,“王邦彦”,13311111)对象,p4指向(123456,“王邦彦”,13311112)对象。如果按照原来Object提供的默认equals方法,那么p3和p1是返回false的,这不满足我们实际生产需要。所以我们需要将equals方法进行重写(覆写)。

public boolean equals(Object arg0) {
		// TODO Auto-generated method stub
		if (arg0==this) {
			return true;
		}
		if (arg0 instanceof Person) {
			Person person=(Person)arg0;
			if (person.hashCode()==this.hashCode()) {
				return true;
			}
			if (person.getId()==this.getId()&&person.getName().equals(this.getName())&&person.getPhoneNo()==this.getPhoneNo()) {
				return true;
			}
		}
		return false;
	}

重写时套路都是一个模板,先进行地址判断,如果和自己相同那肯定是true,然后判断对象是不是自己所在类的实例,如果是则将其转型为自己类对象,判断hash值是否一致,最后再使用“杀手锏”判断类中各个属性是否相同。注意代码中person.getName().equals调用的是String类中重写的equals方法。
我们再来看下String类是如何重写equals方法的:

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;
    }
    ```

是不是同样的套路?,不过String类的“杀手锏”是通过将字符数组中的字符进行单独比较。

getClass()

从字面上看,应该是获取该对象的类。
查看源码

public final native Class<?> getClass();

可以看到,它是有native关键字进行修饰的,什么是native关键字呢?凡是一种语言,都希望是纯。比如解决某一个方案都喜欢就单单这个语言来写即可。Java平台有个用户和本地C代码进行互操作的API,称为Java Native Interface (Java本地接口)。native的意思就是通知操作系统,这个函数你必须给我实现,因为我要使用。所以native关键字的函数都是操作系统实现的,java只能调用。[1]
既然从源码方面我们看不到什么,那么只好实际调用一番来观察getClass到底是什么东西。以equals的代码为例

public class DeepCopy {
	public static void main(String[] args) throws Exception  {
		School_D school_D=new School_D("武汉大学");
		Person_D p1=new Person_D("小明", 11, "张家界",school_D);
		System.out.println(p1.getClass());
		System.out.println(school_D.getClass());
	}
}

结果是 在这里插入图片描述这和我们从字面上看是一致的。
以下是我对其实现的猜测,如有错误还望大家能够指出。
jvm在加载一个类的时候,会先在方法区中寻找是否有这个类信息,如果没有则到/path路径下寻找.class文件,找不到则产生异常ClassNotFonudException,找到了则将其通过类加载器加载至方法区。而实例一个类对象的时候,会将此类的类加载器的引用一并存在方法区中。getClass方法很可能是通过这种方式找到类信息。

hashCode()

从字面上看是获取此对象的哈希值
源码

 public native int hashCode();

和getClass一样都是本地方法。

public class DeepCopy {
	public static void main(String[] args) throws Exception  {
		School_D school_D=new School_D("武汉大学");
		Person_D p1=new Person_D("小明", 11, "张家界",school_D);
		System.out.println(p1.hashCode());
		System.out.println(school_D.hashCode());
	}
}

在这里插入图片描述
可以看出,hashCode方法是将对象进行hash运算,产生一串int类型的值。

HashMap中内部类Node里面有个hash属性,指的就是对象的hash值,那它又是怎么利用hashCode方法呢,观察源码

 public final int hashCode() {
            return Objects.hashCode(key) ^ Objects.hashCode(value);
        }
        
    /**
     * Computes key.hashCode() and spreads (XORs) higher bits of hash
     * to lower.  Because the table uses power-of-two masking, sets of
     * hashes that vary only in bits above the current mask will
     * always collide. (Among known examples are sets of Float keys
     * holding consecutive whole numbers in small tables.)  So we
     * apply a transform that spreads the impact of higher bits
     * downward. There is a tradeoff between speed, utility, and
     * quality of bit-spreading. Because many common sets of hashes
     * are already reasonably distributed (so don't benefit from
     * spreading), and because we use trees to handle large sets of
     * collisions in bins, we just XOR some shifted bits in the
     * cheapest possible way to reduce systematic lossage, as well as
     * to incorporate impact of the highest bits that would otherwise
     * never be used in index calculations because of table bounds.
     */
    static final int hash(Object key) {
        int h;
        return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
    }

它将key的hash值和value的hash值进行了异或操作,进一步避免了哈希冲突的发生。充分利用了HashMap中桶的数量。

Notify(),NotifyAll(),Wait()

用于多线程编程中,对线程进行休眠及唤醒操作。
一个线程中的某个同步共享对象执行wait方法,该线程就释放了该对象的对象锁,进入对象等待池,等待被唤醒;在另一个线程中,这个同步共享变量执行notify方法,唤醒因wait而正在等待使用该对象的线程,使其进入对象锁等待池,有机会获得对象锁,等到获取对象锁,该线程获得CPU调度,继续运行。需要注意wait、notify以及notifyall方法必须在synchronized代码块中,切记![2]

public class WaitAndNotify{
	ReentrantLock rLock=new ReentrantLock();
	private List<Integer> MyList=new ArrayList<Integer>();

	public static void main(String[] args) {
		WaitAndNotify waitAndNotify=new WaitAndNotify();
		threadA t1=new threadA(waitAndNotify.rLock,waitAndNotify.MyList);
		threadB t2=new threadB(waitAndNotify.rLock,waitAndNotify.MyList);
		Thread thread1=new Thread(t1);
		Thread thread2=new Thread(t2);
		thread1.start();
		thread2.start();
		
	}
}
class threadA implements Runnable{
	ReentrantLock rLock=null;
	List<Integer> MyList=new ArrayList<Integer>();

	public threadA(ReentrantLock lock,List<Integer> list) {
		// TODO Auto-generated constructor stub
		this.rLock=lock;
	}
	public void run() {
        try {
            synchronized (rLock) {
            	
                if (MyList.size() != 5) {
                    System.out.println("wait begin "
                            + System.currentTimeMillis());
                    Thread.sleep(2000);
                    rLock.wait();				//wait()方法执行后,将锁释放,threadB获得锁开始循环
                    System.out.println("wait end  "
                            + System.currentTimeMillis());
                }
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

}
class threadB implements Runnable{
	ReentrantLock rLock=null;
	List<Integer> MyList=new ArrayList<Integer>();
	public threadB(ReentrantLock lock,List<Integer> list) {
		// TODO Auto-generated constructor stub
		this.rLock=lock;
	}
	public void run() {
        try {
            synchronized (rLock) {
                for (int i = 0; i < 10; i++) {
                    MyList.add(i);
                    if (MyList.size() == 5) {
                        rLock.notify();                     //notify方法调用之后,不会马上释放锁,而是运行完该同步方法或者是运行完该同步代码块的代码
                        System.out.println("已发出通知!");
                    }
                    System.out.println("添加了" + (i + 1) + "个元素!");
                    Thread.sleep(1000);
                }
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

}

在这里插入图片描述

notify之后并不像wait一样立即释放资源,而是等线程剩余任务完成后才交出锁对象和CPU。
wait()方法可以传入参数也可以不传入参数,传入参数就是在参数结束的时间后开始等待,不传入参数就是直接等待。

Sleep()与Wait()区别

sleep方法是Thread中的静态方法。public static native void sleep(long millis) throws InterruptedException;导致此线程暂停执行指定时间,给其他线程执行机会,但是依然保持着监控状态,过了指定时间会自动恢复,调用sleep方法不会释放锁对象。
当调用sleep方法后,当前线程进入阻塞状态。目的是让出CPU给其他线程运行的机会。但是由于sleep方法不会释放锁对象,所以在一个同步代码块中调用这个方法后,线程虽然休眠了,但其他线程无法访问它的锁对象。这是因为sleep方法拥有CPU的执行权,它可以自动醒来无需唤醒。而当sleep()结束指定休眠时间后,这个线程不一定立即执行,因为此时其他线程可能正在运行。
wait方法是Object类里的方法,当一个线程执行到wait()方法时,它就进入到一个和该对象相关的等待池中,同时释放了锁对象,等待期间可以调用里面的同步方法,其他线程可以访问,等待时不拥有CPU的执行权,否则其他线程无法获取执行权。当一个线程执行了wait方法后,必须调用notify或者notifyAll方法才能唤醒,而且是随机唤醒,若是被其他线程抢到了CPU执行权,该线程会继续进入等待状态。由于锁对象可以时任意对象,所以wait方法必须定义在Object类中,因为Obeject类是所有类的基类。
[3]

toString()

返回对象的信息,默认情况下是返回对象的类信息+hash值(16进制)

public class DeepCopy {
	public static void main(String[] args) throws Exception  {
		School_D school_D=new School_D("武汉大学");
		Person_D p1=new Person_D("小明", 11, "张家界",school_D);
		System.out.println(p1.hashCode());
		System.out.println(p1.toString());
		int a=p1.hashCode();
		System.out.println(a);
	}
}

在这里插入图片描述
当有特殊需要时,可以对其进行重写。

public class DeepCopy {
	public static void main(String[] args) throws Exception  {
		School_D school_D=new School_D("武汉大学");
		Person_D p1=new Person_D("小明", 11, "张家界",school_D);
		System.out.println(p1.toString());
	}
}
@Override
	public String toString() {
		return "姓名是:"+this.name+"年龄:"+this.age+"住址"+this.address+"  学校是"+this.school;	
	}

在这里插入图片描述

参考资料

[1]https://www.cnblogs.com/KingIceMou/p/7239668.html
[2]https://www.cnblogs.com/hzhtracy/p/4636185.html
[3]https://www.cnblogs.com/lyx210019/p/9427146.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值