Object类是Java中所有类的祖先,在Java中每个类都是由它拓展而来,我们都知道继承一个类的写法
class child extends father
但是继承Object类并不需要写
class child extends Object
因为如果你不明确指定继承的类,Object就被默认为是这个类的父类。
由于在Java中,每个类都是由Object类拓展而来的,所以熟悉这个类所提供的服务就有必要认真的去观摩观摩了。
1、Equals方法
在Objdect类中的equals方法用于检测一个对象是否等于另外的一个对象,如果两个对象具有相同的引用,则相等。下面用一个例子来解读。
class Employee
{
public Employee(String name,double salary,int year,int month,int day) {
this.name = name;
this.salary = salary;
GregorianCalendar calendar = new GregorianCalendar(year,month-1,day);
this.hireDay =calendar.getTime();
}
public String getName()
{
return this.name;
}
public double getSalary()
{
return this.salary;
}
public Date getHireDay()
{
return this.hireDay;
}
public void raiseSalary(double byPercent)
{
double raise = salary * byPercent / 100;
salary += raise;
}
public boolean equals(Object otherObject)
{
if(this==otherObject)return true;
if(otherObject==null)return false;
if(getClass()!=otherObject.getClass())return false;
Employee other = (Employee)otherObject;
return this.name.equals(other.name)&&this.salary==other.salary&&this.hireDay.equals(other.hireDay);
}
public int hashCode()
{
return 7*name.hashCode()+11*new Double(salary).hashCode()+13*hireDay.hashCode();
}
/*public String toString()
{
return getClass().getName()+"[name="+name+",salary="+salary+",hireDay"+hireDay+"]";
}*/
private String name;
private double salary;
private Date hireDay;
}
class Manage extends Employee
{
public Manage(String name,double salary,int year,int month,int day)
{
super(name,salary,year,month,day);
this.bonus = 0;
}
public double getSalary()
{
double baseSalary = super.getSalary();
return baseSalary+bonus;
}
public void setBonus(double b)
{
bonus = b;
}
public boolean equals(Object otherObject)
{
if(!super.equals(otherObject))return true;
Manage other = (Manage)otherObject;
return bonus == other.bonus;
}
public int hashCode()
{
return super.hashCode()+17*new Double(bonus).hashCode();
}
/*public String toString()
{
return super.toString()+"[bonus="+bonus+"]";
}*/
private double bonus;
}
这里定义了两个类,一个Employee类和一个子类Manage类,当我们创建两个对象的时候
Employee alice1 = new Employee("Alice Adams",75000,1987,12,15);
Employee Bob = new Employee("Bob",50000,1989,1,25);
System.out.println("alice1.equals(alice3):"+alice1.equals(alice3));
System.out.println("alice1.equals(Bob):"+alice1.equals(Bob));
...
alice1.equals(alice3):true
alice1.equals(Bob):false
1)可以看到输出的结果alice1和alice指向的是同一个引用,所以结果是对的,而alice1和Bob指向的不是同一个对象,所以不相等。
2)当遇到的参数不属于同一个类的时候equals应该怎么做呢?比如在Employee和Manage对象中都有相同的姓名、薪水和雇佣日期。我们可以这样定义equals
public boolean equals(Object otherObject)
{
if(this==otherObject)return true;//检测this和otherObeject引用的是否同一个对象
if(otherObject==null)return false;//检测otherObject是否为空
if(getClass()!=otherObject.getClass())return false;//比较this和otheObject是否是同一个类的
Employee other = (Employee)otherObject;//将otherObject转换成为响应的类的类型变量
return this.name.equals(other.name)&&this.salary==other.salary&&this.hireDay.equals(other.hireDay);//开始做字段的匹配
}
//如果在子类中重新定义equals,就要在其中包含调用super.equals(other)
public boolean equals(Object otherObject)
{
if(!super.equals(otherObject))return true;
Manage other = (Manage)otherObject;
return bonus == other.bonus;
}
equals和“==”的区别
==是判断两个变量或实例是不是指向同一个内存空间
equals是判断两个变量或实例所指向的内存空间的值是不是相同
当这两者除了在String类型和封装器上的比较有区别,其他并没有区别,因为String类型重写了equals。
String下的equals源码:
所以在这里面,equals()指比较字符串或封装对象对应的原始值是否相等,"=="是比较两个对象是否为同一个对象。
StringBuilder sb1 = new StringBuilder("aaa");
StringBuilder sb2 = new StringBuilder("aaa");
System.out.println(sb1.equals(sb2)+" "+ (sb1==sb2));//输出false false,因为是对象的比较,equals和“==”都是比较引用
String sb1 = new String("aaa");
String sb2 = new String("aaa");
System.out.println(sb1.equals(sb2)+" "+ (sb1==sb2));//输出 true false.因为String重写了equals,equals比较的是值。
2、HashCode方法
HashCode是由对象导出的一个整型值,(散列码是由内容导出的)如果x和y 是不同的对象,则他们的散列值一般是不同的。
在Object类中的hashCode方法默认导出的是对象的存储地址。
另Equals和hashCode的定义必须一致,如果x.equals(y)返回true,那么x和y的hashCode应该具有相同的值。
比如:
StringBuilder sb1 = new StringBuilder(“aaa”);
StringBuilder sb2 = new StringBuilder(“aaa”);
System.out.println(sb1.equals(sb2)+" "+ (sb1==sb2));
System.out.println("alice1.hashCode():"+alice1.hashCode());
System.out.println("alice3.hashCode():"+alice3.hashCode());
public int hashCode()
{
return 7*name.hashCode()+11*new Double(salary).hashCode()+13*hireDay.hashCode();//与equals有同样的字段进行计算,计算规则自定义,但是应该让散列码更均匀
}
//输出结果
alice1.hashCode():377780067
alice3.hashCode():377780067
//如果我们将上面的方法给注释了,则返回的是存储地址的值
alice1.hashCode():1028566121
alice3.hashCode():1118140819
3、toString方法***
在Object中还有一个很重要的方法就是toString方法。它用于返回表示对象值得字符串。在Object中我们可以看到它是这样定义的
/**
* Returns a string representation of the object. In general, the
* {@code toString} method returns a string that
* "textually represents" this object. The result should
* be a concise but informative representation that is easy for a
* person to read.
* It is recommended that all subclasses override this method.
*
* The {@code toString} method for class {@code Object}
* returns a string consisting of the name of the class of which the
* object is an instance, the at-sign character `{@code @}', and
* the unsigned hexadecimal representation of the hash code of the
* object. In other words, this method returns a string equal to the
* value of:
** * getClass().getName() + '@' + Integer.toHexString(hashCode())
*
*
* @return a string representation of the object.
*/
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
alice1=alice2:Employee@3d4eac69,Employee@3d4eac69
alice1=alice3:Employee@3d4eac69,Employee@42a57993
它返回的是一个类名和散列码,但是返回这个值有点太抽象,对我们的意义并不是很大,我们可以对这个方法进行重载,返回我们想要的字段
public String toString()
{
return getClass().getName()+"[name="+name+",salary="+salary+",hireDay"+hireDay+"]";
}/**/
//输出结果
alice1=alice2:Employee[name=Alice Adams,salary=75000.0,hireDayTue Dec 15 00:00:00 CST 1987],Employee[name=Alice Adams,salary=75000.0,hireDayTue Dec 15 00:00:00 CST 1987]
alice1=alice3:Employee[name=Alice Adams,salary=75000.0,hireDayTue Dec 15 00:00:00 CST 1987],Employee[name=Alice Adams,salary=75000.0,hireDayTue Dec 15 00:00:00 CST 1987]
对于数组类型的域,equals、hashcode和都可以应用静态的Arrays调用来检测。
4、class getClass()
返回包含对象信息的类的对象,
5、protected native Object clone()
拷贝与克隆的区别
拷贝:拷贝的变量与原变量指向同一个对象
Employee original = new Employee("John Public",50000);
Employee copy = original;
copy.raiseSalary(10);//则原始的original的Salary也会改变
克隆:创建一个对象的副本。Java运行时系统将为新实例分配存储控制,并不是只创建一个变量指向相同的空间哦,而是有开辟新的存储空间。
Employee copy = original.clone();
copy.raiseSalary(10);//original的salary并没有改变
但是事情并没有那么简单,clone是一个Proteced方法,也就是说用户编写的代码不能直接访问,如果对象中的所有数据域都属于基本数值或基本类型,则不会出现什么情况,但是如果对象中包含对子对象的引用,则克隆结果会使两个域引用同一个子对象,
所以针对这种情况,我们要对clone进行重定义,将其定义为public类型。
class Employee implements Cloneable()
{
public Employee clone() throws CloneNotSupportedException
{
Employee cloned = (Employee)super.clone();
cloned.hireDay = (Date)hireDay.clone();//所有可变的域都应该克隆
return cloned;
}
}
6、void notify(),voide notifyall(),void wait(long timeout)
这三者一般应用在同步synchronized关键字声明的方法中,如果一个方法用synchronized关键字声明后,那么对象的锁将保护整个方法
public synchronized void method()
{
method body
}
而wait 和notify,notifyall用在这个方法中。
void wait(long timeout): 导致线程进入等待状态知道它被通知,该方法只能在一个同步方法中调用。如果当前线程不是对象锁的持有者,该方法抛出一个IllegalMonitorStatwException异常
voide notifyall():解除那些在该对象上调用wait方法的线程的阻塞状态,该方法只能在同步方法或同步块内部调用,如果当前线程不是对象锁的持有者,则抛出IllegalMonitorStatwException异常
voide notify():随机选择一个在该对象上调用wait 方法的线程,解除其阻塞状态,该方法只能在同步方法或同步块内部调用,如果当前线程不是对象锁的持有者,则抛出IllegalMonitorStatwException异常