目录
object类除了object()构造方法外,还提供了11个成员方法
根据jdk-1.8API中可见
也就是如下表格所示:
Modifier and type | method | description |
protected Object | clone() | 创建并返回该对象的副本 |
boolean | equals(Object obj ) | 指示一些其他对象是否等于此 |
protected void | finalize() | 当垃圾收集确定不再有对该对象的引用时,垃圾收集器在对象上调用 |
类<?> | getClass() | 返回此Object的运行时类 |
int | hashCode() | 返回对象的哈希码值 |
void | notify() | 唤醒正在等待对象监视器的单个线程 |
void | notifyAll() | 唤醒正在等待对象监视器的所有线程 |
String | toString() | 返回对象的字符串表示形式 |
void | wait() | 导致当前线程等待,直到另一个线程调用该对象的notify()或notifyAll()方法 |
void | wait(long timeout) | 导致当前线程等待,直到另一个线程调用该对象的notify()或notifyAll()方法,或指定时间已过 |
void | wait(long timeout,int nanos) | 导致当前线程等待,直到另一个线程调用该对象的notify()或notifyAll()方法,或者某些其他线程中断当前线程,或一定量的实习时间 |
1、clone()方法
这个方法是为object克隆对象而生的,它不是简单的copy ,也不是简单的new 一个对象,clone()方法存在的意义在于Java语言的对象引用机制
如:
Person p1 = new Person();
Person p2 = p1;
此时,p1和p2引用的是同一个对象,这个对象在jvm的堆内存中是共享的,也就是说如果P2改变,那么p1也会随之改变。
那么要避免p1随着p2改变,应该怎么做?
------使用clone()方法。这个方法可以在堆中克隆出另外一个对象,这样p1不会随着p2改变,因为他们引用的不再是同一个对象。
注意:首先要实现Cloneable接口,然后在类中重写Object类中的clone方法
代码示例如下:
public class CloneTest implements Cloneable {
public static void main(String[] args) {
Person p1 = new Person();
p1.name="mq";
p1.age=22;
Person p2 = p1;
p2.name="mq2";
System.out.println("p1=="+p1.toString());
System.out.println("p2=="+p2.toString());
}
}
//输出结果p1==Person{name='mq2', age=22}--p2==Person{name='mq2', age=22}
//在Person中重写clone()方法
class Person implements Cloneable {
String name;
int age;
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
protected Object clone() throws CloneNotSupportedException {
// TODO Auto-generated method stub
Person p=null;
p=(Person) super.clone();
return p;
}
//修改测试类代码
Person p2 = null;
try {
p2 = (Person) p1.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
p2.name="mq2";
//输出结果:p1==Person{name='mq', age=22}--p2==Person{name='mq2', age=22}
2、equals()和hashCode()方法
在Java中,有时候判断两个对象之间的相等关系,并不能仅仅通过“=”或“==”来进行判定。
“==”只是比较两个对象的地址是否一致,当然如果返回为true,那当然相等,地址相同肯定是同一对象。
但大部分时候,我们比较俩对象时,并不是将引用同一对象的引用来进行比较的。如,随便new两个对象进行比较,新new的两个对象地址肯定不同,这时候“==”返回的就是false。
那是否说明两个对象不同呢?
并不能说明,程序里面的对象是否相等,并不是但从地址是否一致来考量的,还要从程序员的比较和考量的角度(判断相等的标准由程序员决定)
这时候就要重写equals()、hashCode()方法了
hashcode()方法其实就是对应对象的地址,调用它的时候,它的返回值就是对象的地址(在hash表中对应的位置)。
equal()方法则是比较两个对象的具体方法描述,或者说规则。也就是说,你要比较两个对象是否相等。你的比较原则是什么,怎么去比较,就重写在equal()方法中。
equals()方法和hashcode的关系:
-
如果两个对象equals相等,那么这两个对象的HashCode一定也相同
-
如果两个对象的HashCode相同,不代表两个对象就相同,只能说明这两个对象在散列存储结构中,存放于同一个位置
为什么重写equals()方法后,还建议重写hashCode()方法?
-
使用hashcode方法提前校验,可以避免每一次比对都调用equals方法,提高效率
-
保证是同一个对象,如果重写了equals方法,而没有重写hashcode方法,会出现equals相等的对象,hashcode不相等的情况,重写hashcode方法就是为了避免这种情况的出现。
代码示例:
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Student student = (Student) o;
return sno.equals(student.sno) && name.equals(student.name);
}
@Override
public int hashCode() {
int hash=name.hashCode()+sno.hashCode()+1;
return hash;
}
3、finalize()方法
finalize()方法是专供垃圾回收提供的,在GC回收某个对象的时候,首先会调用该对象的finalize()方法。
当一个堆空间中的对象没有被栈空间变量指向的时候,这个对象会等待被JVM回收
只是告诉java这个调用对象可以回收,但不能强制启动GC进行回收,jvm会在合适的时候进行回收(内存不足时)
protected void finalize() throws Throwable{
}
//jdk9以后过时
4、getClass()
此方法与反射有关。用于返回一个返回该对象的运行时类的java.lang.Class对象。也就是运行中的对象本身。
Student stu1 = new Student();
Class stuClass = stu1.getClass();
System.out.println(stuClass.getName());
5、notify、notifiAll和wait()
这三个方法是用来实现多线程的。
wait():来阻塞线程,wait()被调用后,线程将会进行等待状态,锁被释放,同时线程也会让出cpu资源,其他的线程可以运行。
notify()\notifyAll():方法用于唤醒线程,调用notify\notifyAll方法后,不会立即释放锁,而是等待sychronize代码块执行完后。或者遇到wait()方法后,在释放锁。
因此,有一点要非常注意,notify、notifiAll和wait()是必须在sychronize包含的代码快中使用。这跟thread类的sleep方法是不一致的,sleep()方法可以在任意地方使用。
6、toString()方法
返回对象的字符串表现形式
若没有覆盖toString(),在使用对象直接输出的时候,默认输出的是一个对象在堆内存上的地址值。
public class toStringTest {
public static void main(String[] args) {
People p1 = new People("MQ",18);
System.out.println("未覆盖toString()输出:"+p1);
}
}
class People{
private String name;
private int age;
public People(String name, int age) {
this.name = name;
this.age = age;
}
}
>>>输出:未覆盖toString()输出:test.People@4554617c
//覆盖toString()后
@Override
public String toString() {
return "People{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
>>>输出:覆盖toString()输出:People{name='MQ', age=18}
提问:为什么字符串可以通过+进行不同类型数据拼接?
System.out.println("mq"+666);
>>>输出:mq666
Object是所有类的父类,任意类都是继承Object类的。而Object中定义了 toString()
方法,所以任意类中都包含了toString()
方法,对象在实例化之后都可以调用。
String作为信息输出的重要数据类型,在Java中所有的数据类型只要遇见String就执行了+
,都要求其变为字符串后连接,而所有对象想要变为字符串就默认用toString( )方法