Object类是所有Java类的根父类。
如果在类的声明中未使用extends关键字指明其父类,则默认父类为java.lang.Object类
先来看看帮助文档的介绍:
clone()方法
protected native Object clone() throws CloneNotSupportedException;
clone方法的作用就是创建并返回对象的一个副本,也就是copy。
创建同一个类的一个新对象,并将原有对象的属性值赋给新的对象(浅拷贝)。
一个类的对象想要调用clone方法,该类必须实现Cloneable接口,否则会抛出CloneNotSupportedException异常,然后重写clone方法,调用父类的clone方法,返回克隆的对象(super.clone())。数组默认是支持clone的。
public class ObjectTest01 {
public static void main(String[] args) {
Student student1 = new Student();
String[] hobbies = {"足球","篮球","羽毛球"};
student1.setAge(18);
student1.setName("张三");
student1.setHobbies(hobbies);
Student student2 = (Student) student1.clone();
System.out.println(student1);
System.out.println(student2);
}
}
class Student implements Cloneable{
private int age;
private String name;
private String[] hobbies;
public Student(){
super();
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String[] getHobbies() {
return hobbies;
}
public void setHobbies(String[] hobbies) {
this.hobbies = hobbies;
}
@Override
public Object clone() {
try{
return super.clone();
}catch (CloneNotSupportedException e){
e.printStackTrace();
}
return null;
}
@Override
public String toString() {
return "Student@" + this.hashCode() + "{" +
"age=" + age +
", name='" + name + '\'' +
", hobbies=" + Arrays.toString(hobbies) +
'}';
}
}
打印结果如下:
此时成功拷贝了student对象,但是是浅拷贝,就是赋值操作,如果对引用类型的属性中的值进行操作,原对象中的值也会被改变。如果想要实现完全的拷贝,深拷贝,只需要将引用类型的属性(成员变量)也clone一遍进行赋值即可,当然该成员变量也必须实现Cloneable接口,重写clone方法。
clone方法修改如下:
public Object clone() {
try {
Student student = (Student) super.clone();
student.setHobbies(student.getHobbies().clone());
return student;
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return null;
}
toString()方法
public String toString();
当我们使用System.out.println()打印对象时,就是调用的toString方法。
如果不重写toString(),那么默认调用的就是Object类中的toString方法,输出对象类型及地址。
但是我们在实际打印对象时,往往希望看到的是对象中的内容,而不是对象的地址,所以我们需要重写该方法。
public class Person{
private int age;
private String name;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Person{" +"age=" + age +", name='" + name +"}";
}
}
getClass()方法
public final native Class<?> getClass();
getClass()名称和getXX()类似,作用是获取当前对象的运行时类对象。通过类对象可以获取到类中的属性,方法,名称,类加载器等内容。
public class GetClassTest {
public static void main(String[] args) {
String s = "1111";
System.out.println(s.getClass());
//s.getClass().getDeclaredField();
//s.getClass().getMethod();
//s.getClass().getClassLoader();
}
}
equals()方法
public boolean equals(Object obj){}
Object类中equals方法的底层就是用运算符"=="。
比较的是两个对象的地址值是否相同。在实际开发中,常常需要重写equals方法(一般比较对象的内容)。
比如:
public class Student{
private int age;
private String name;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Overried
public boolean equals(Object obj){
if(this == student){
return true;
}
if(obj != null && this.getClass() == obj.getClass()){
Student student1 = (Student) obj;
//如果属性也是引用类型,那么需要调用重写后的equals来比较
return age == worker.age && name.equals(worker.name);
}
return false;
}
}
hashCode()方法
public native int hashCode();
hashCode()方法作用是返回对象的哈希码值。这个方法是为了支持哈希表结构,例如HashMap。
注意:
- 一次java程序的执行中,同一个对象多次调用hashCode方法,产生的哈希码值一定是相同的,前提是,前提是将对象进行equals比较时所用的信息没有被修改。但是,在多次启动相同的java程序时,同一对象调用hashCode方法产生的哈希值不需要保持相同。
- 如果根据equals方法两个对象相等,那么它们的哈希码值一定是相同的;如果equals方法比较两个对象不相等,那么哈希码值一定不同。但是拥有相同哈希码值的对象不一定相等。
所以重写equals方法时,通常也需要重写hashCode方法,以维护其通用性。
public class HashCodeTest {
public static void main(String[] args) {
String s = "1111";
System.out.println("s: " + s.hashCode());
String s1 = "2222";
System.out.println("s1: " + s1.hashCode());
System.out.println("s == s1? " + s.equals(s1));
String s2 = "2222";
System.out.println("s1 == s2? " + s1.equals(s2));
System.out.println("s2: " + s2.hashCode());
}
}
notify()方法和notifyAll()方法
public final native void notify();
唤醒单个当前正在等待对象监视器的线程,且唤醒的选择性是任意的。
public final native void notifyAll();
唤醒当前正在对待对象监视器的所有线程。
wait()方法
public final native void wait(long timeout) throws InterruptedException;
让当前线程等待,直到被当前对象的其它线程调用notify()或者notifyAll()唤醒,或者等待超过指定时间。timeout单位为毫秒
public final void wait() throws InterruptedException {
wait(0);
}
让当前线程等待,直到被当前对象的其它线程调用notify()或者notifyAll()唤醒。这里实际就是调用上一个方法,但是指定时间为0ms。
public final void wait(long timeout, int nanos) throws InterruptedException{
if (timeout < 0) {
throw new IllegalArgumentException("timeout value is negative");
}
if (nanos < 0 || nanos > 999999) {
throw new IllegalArgumentException(
"nanosecond timeout value out of range");
}
if (nanos > 0) {
timeout++;
}
wait(timeout);
}
让当前线程等待,直到被当前对象的其它线程调用notify()或者notifyAll()唤醒,或者经过了指定的时间。
wait(long timeout, int nanos)方法和wait(long timeout)方法功能是相同的,只是前者能够更精准地控制时间。timeout单位是毫秒,nanos的单位是纳秒,且输入范围是[0.999999],即不超过1毫秒。
源码上面的注释写的是以纳秒为单位的等待时间由:1000000*timeout+nanos 给出,但实际上只要传入的参数nanos大于0时,等待时间就增加1ms。这里是jdk8版本的代码,可能是由于纳秒级的等待时间太短,直接加1ms。
在更早的版本中:
public final void wait(long timeout, int nanos) throws nterruptedException {
if (timeout < 0) {
throw new IllegalArgumentException("timeout value is negative");
}
if (nanos < 0 || nanos > 999999) {
throw new IllegalArgumentException(
"nanosecond timeout value out of range");
}
if (nanos >= 500000 || (nanos != 0 && timeout == 0)) {
timeout++;
}
wait(timeout);
}
当毫秒级等待时间timeout为0且纳秒级等待时间nanos不为0时,等待时间增加1ms。
当nanos大于等于半毫秒时,等待时间增加1ms。
除以上两种情况以外,等待时间不做额外的增加。
finalize()方法
protected void finalize() throws Throwable{}
我们创建的对象都是存储在堆空间中的,并且由引用类型的变量指向这些对象。但是,如果一味地创建对象会导致堆空间被占满甚至溢出,所以java提供了垃圾回收机制。当一个对象没有任何引用指向它时,就认为它是没有用了,这时垃圾回收机制会被触发对其进行回收操作。但是,垃圾回收机制给这些无用的对象一个喘息的机会,在实际回收这些对象之前,会调用finalize()方法。如果重写了finalize方法,让新的引用变量指向要回收的对象,此时这些对象又将被激活,而不被回收。
测试一下:
public class FinalizeTest {
public static void main(String[] args) {
People people = new People("chunni",18);
System.out.println(people);
people = null;//此时没有引用执行该对象了
System.gc();//强制释放对象,通知垃圾收集器回收该对象
}
}
class People{
private String name;
private int age;
public People() {
}
public People(String name, int age) {
this.name = name;
this.age = age;
}
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;
}
@Override
protected void finalize() throws Throwable {
System.out.println("准备回收对象" + this);
//People people = this;
}
@Override
public String toString() {
return "People{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
finalize方法中可以进行一切操作,甚至重新激活要回收的对象。但是一般调用finalize方法后就进行清理操作,且由对象自动调用。不建议手动调用finalize方法。