前言
- 爱美食的程序媛,能下厨房能敲代码,哈哈
- 很少看源码,最近开始研究,或许有理解错误的地方,还望指正。
概述
Java中所有类的祖先,Java中所有的类包括我们自己定义的一些类,都会显式或隐式的继承Object类,任何一个类都可以调用Object中的非私有方法。
相关方法
1. registerNatives():void
private static native void registerNatives();
static {
registerNatives();
}
- Object类的私有方法,不会被子类继承,并在静态代码块中执行,也就是在类进行初始化的时候就会执行
- 使用了native方法修饰,说明这个方法是个原生函数,也就是底层是使用其他语言实现的,在JDK的源码中并不包含这些函数,主要是实现了像分配内存空间之类的这些操作
2. getClass():Class<?>
public final native Class<?> getClass();
- 同样使用native修饰,不允许重写,用来获取运行时对象(JVM内部做的)的class类实例,常用于反射。类似的还有:Class.forName(“类的全包名”)、类名.class。
3. hashCode():int
public native int hashCode();
- 返回该对象的哈希码值,int类型,通过内部转换,JDK源码的注解中说到默认是将对象的内存地址转换为一串整数(不是使用Java语言实现,具体需要导出Object的JNI头文件查看,此处不做深入研究)
- 在应用程序的执行期间,一个对象没有被修改的前提下,多次调用时,hashCode必须返回相同的整数,如果两个对象的equals相等,那么hashCode一定是相等的,如果equals不相等,hashCode可能相等也可能不相等。
- hashCode主要是用于哈希表中,比如HashMap、HashSet。
4. equals(Object):boolean
public boolean equals(Object obj) {
return (this == obj);
}
- 从上面的代码可以看出来,equals的判断使用了= =用来判断当前调用equals的对象和参数对象是同一个对象的,也就是说判断这两个对象指向的内存地址是否是同一块,如果是就返回true,否则返回false,就算内容完全相同,内存地址不同也返回false。
- 不重写的话没有什么太大的意义,底层还是直接使用的= =判断,直接使用==也可以判断内存地址是否相同,一般都会重写(比如String类)。
5. clone():Object
protected native Object clone() throws CloneNotSupportedException;
- 也是本地方法,用来复制对象的,当一个对象调用该方法时,会返回一个与调用对象一摸一样的克隆对象
- 该方法被声明为protected受保护的,也就是只有继承了这个类才可以使用这个方法,可以由子类重写该方法,扩大方法的访问范围。
- 如果需要调用的clone()方法的话,调用类需要实现Cloneable接口(接口没有方法,类似于一个标志,标志可以使用clone方法),否则就会抛出异常提示无法克隆,比如之前有写过的集合类:ArrayList、LinkedList都有实现Cloneable接口。
- 此处又涉及了深拷贝和浅拷贝,浅拷贝:外表变了,指向的还是原来的地址。深拷贝,把所有的东西都克隆一份,并且指向的不是同一个地址。那么Object的clone()是浅拷贝还是深拷贝呢,用代码测试一下看看
//Order类
@Data
public class Order {
private int id;
}
//user类
@Data
public class User implements Cloneable{
private String userName;
private String userId;
private Order order;
public Object clone(){
Object o=null;
try {
o=super.clone();
} catch (CloneNotSupportedException e) {
System.out.println(e.toString());
}
return o;
}
}
//测试类
public class test {
public static void main(String[] args) {
User user=new User();
Order order=new Order();
order.setId(1);
user.setOrder(order);
user.setUserId("123");
user.setUserName("JudyTest");
System.out.println("克隆前:"+user.getUserName()+"--"+user.getOrder().getId());
User user1=(User)user.clone();
user1.setUserName("judyTest1");
user1.getOrder().setId(2);
System.out.println("克隆后:"+user.getUserName()+"--"+user.getOrder().getId());
}
}
输出结果:
可以看到,如果是单个对象的话,那么是一种深拷贝,如果是对象的属性也是对象的话,那么对于这个属性对象来说又是一种浅拷贝。
6. toString():String
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
- 就是把对象转换成一个字符串,如果不重写的话,默认输出的就是实例对象的类名+对象hashCode的十六进制,没有什么太大的意义,一般是重写,可以知道类里面的实际内容是什么
7. notify()、notifyAll()、wait():
都是在多线程时使用到的方法,为了配合synchronized使用的
//随机唤醒该对象等待集合中的任意一条线程
public final native void notify();
//唤醒该对象等待集合中的所有线程
public final native void notifyAll();
//调用wait()方法会导致当前对象等待,只有过了指定的时间,或者其他的线程调用了notify或notifyAll方法才会结束等待
public final native void wait(long timeout) throws InterruptedException;
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);
}
public final void wait() throws InterruptedException {
wait(0);
}
8. finalize():void
protected void finalize() throws Throwable { }
- protected修饰,受保护的方法,在垃圾回收时会调用对象的finalize方法(GC垃圾回收的时机具有不确定性,可能从头到尾都不会执行到),如果在垃圾回收之前让对象重新指向一个引用,那么就可以避免对象被回收,算是延长了对象的生命周期。但是并不是每次回收都会调用该方法,垃圾回收可能有多次,该方法只会调用一次。
- 显然必须要重写才会有意义,因为Object中的finalize方法什么也没有写
- 什么时候可能会用到,比如该对象处理的是一个非Java资源,像数Connection据库连接这样的忘记了close,就可以在Connection里实现finalize()方法,然后在里面调用close,不过一般情况都不会用到,也靠不住,因为调用了finalize并不代表这个对象就会被立马回收,可能已经调用了但又没有回收,等到下次真的要回收的时候又不能调用了。