一、总体描述
在Java中,Object
类是最基本的类,所有其他的类都是直接或间接地继承自 Object
类。Object
类定义了一些基本的方法,这些方法可以被所有类使用。可以说Object类为Java中的对象提供了通用的行为和特征。
二、对源码的详细分析
Object类中没有成员变量,但是有一些基础的、通用的方法。在实际的使用中,其他继承Object的类经常会重写这些方法,来实现它们自己的功能。下面会挨个介绍对Object类中的方法。
1.registerNatives()
源码:
private static native void registerNatives();
static {
registerNatives();
}
registerNatives()方法是用来将Java类中的本地方法(比如我们自己写代码的时候,在代码中使用了toString()方法)与这些方法对应的本地代码(一般是用c/c++这类语言写的)通过JNI(Java Native Interface)进行绑定的。
具体讲,就是,当Java虚拟机加载含有本地方法的类时,它会找到 registerNatives()
方法,然后用它注册这些本地方法。这样,Java虚拟机就能够知道如何调用这些本地方法的实现。
为了更好地理解,我们可以举一个例子。假如有一场音乐会,只有编写好的音乐(java类中的本地方法)是没法演奏的,需要一个指挥家(Java虚拟机)来指挥(绑定)乐手(本地代码)演奏音乐。“音乐经过指挥家的指挥,被乐手有章法地演奏出来”相当于“我们java类中的本地代码经过操作系统绑定到对应的本地代码,才能被操作系统理解”。
因此,registerNatives()的关键字包括static(静态的)和native(调用本地代码的)。当java虚拟机首次访问一个类的时候,就会执行registerNatives()。这样能让这个java类在被初始化的时候,里面的方法就被正确注册(绑定本地代码)。
2.getClass()
源码:
public final native Class<?> getClass();
用来返回当前类的信息。final方法不能被子类重写(下面其他带final的方法不再挨个标注)。
一些常见的可以获取的内容:
类名:可以通过 getName()
方法获取类的完全限定名称。
简单类名:可以通过 getSimpleName()
方法获取类的简单名称。
构造器:可以通过 getDeclaredConstructors()
和 getConstructors()
方法获取类中声明的所有构造器。
方法:可以通过 getDeclaredMethods()
和 getMethods()
方法获取类中声明的所有方法。
字段:可以通过 getDeclaredFields()
和 getFields()
方法获取类中声明的所有字段。
超类:可以通过 getSuperclass()
方法获取类的直接超类。
接口:可以通过 getInterfaces()
方法获取类实现的所有接口。
包:可以通过 getPackage()
方法获取类所在的包。
注解:可以通过 getAnnotations()
方法获取类上的注解
举例:
public class MyClass {
private int data;
public MyClass(int data) {
this.data = data;
}
public int getData() {
return data;
}
}
public class Main {
public static void main(String[] args) {
MyClass obj = new MyClass(10);
// 使用 getClass() 获取对象的 Class 对象
Class<?> clazz = obj.getClass();
// 打印类名
System.out.println("Class Name: " + clazz.getName());
// 打印简单类名
System.out.println("Simple Class Name: " + clazz.getSimpleName());
// 打印构造器
Constructor<?>[] constructors = clazz.getDeclaredConstructors();
for (Constructor<?> constructor : constructors) {
System.out.println("Constructor: " + constructor);
}
// 打印方法
Method[] methods = clazz.getDeclaredMethods();
for (Method method : methods) {
System.out.println("Method: " + method);
}
// 打印字段
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
System.out.println("Field: " + field);
}
// 打印超类
Class<?> superClass = clazz.getSuperclass();
System.out.println("Superclass: " + superClass.getName());
// 打印接口
Class<?>[] interfaces = clazz.getInterfaces();
for (Class<?> inter : interfaces) {
System.out.println("Interface: " + inter.getName());
}
// 打印包
Package pkg = clazz.getPackage();
System.out.println("Package: " + pkg.getName());
}
}
输出结果:
Class Name: MyClass
Simple Class Name: MyClass
Constructor: public MyClass(int)
Method: public int MyClass.getData()
Field: private int MyClass.data
Superclass: class java.lang.Object
Interface:
Package:
3.hashCode()
源码:
public native int hashCode();
用来返回对象的哈希码。默认的,由Java虚拟机提供的hashCode()方法,会返回对象的内存地址的哈希码。但是在实际应用中,这个方法经常被重写,以便于按照自己希望的方法生成哈希码。
举例:
public class MyClass {
private int data;
public MyClass(int data) {
this.data = data;
}
public int getData() {
return data;
}
@Override
public int hashCode() {
// 重写 hashCode() 方法,基于 data 字段生成哈希码
return Integer.hashCode(data);
}
}
public class Main {
public static void main(String[] args) {
MyClass obj1 = new MyClass(10);
MyClass obj2 = new MyClass(20);
// 使用 hashCode() 方法获取对象的哈希码
int hash1 = obj1.hashCode();
int hash2 = obj2.hashCode();
System.out.println("Hash code of obj1: " + hash1);
System.out.println("Hash code of obj2: " + hash2);
}
}
运行结果:
Hash code of obj1: 10
Hash code of obj2: 20
4.equals(Object obj)
源码:
public boolean equals(Object obj) {
return (this == obj);
}
在默认情况下,equals方法的比较方式是==,也就是比较两个对象的引用。可以重写这个方法来比较别的东西。
5.clone()
源码:
protected native Object clone() throws CloneNotSupportedException;
这是一个用来复制对象的方法。默认情况下,clone()
方法执行浅拷贝,即复制对象本身,但对于对象中的引用类型成员,只复制引用,不复制引用的对象。
另外,调用 clone()
方法之前,如果抛出异常CloneNotSupportedException,则说明调用它的类没有实现Cloneable接口
。
public class MyClass implements Cloneable {
private int id;
private String name;
private Object reference; // 引用类型成员
public MyClass(int id, String name, Object reference) {
this.id = id;
this.name = name;
this.reference = reference;
}
public int getId() {
return id;
}
public String getName() {
return name;
}
public Object getReference() {
return reference;
}
@Override
protected Object clone() throws CloneNotSupportedException {
// 检查是否实现了 Cloneable 接口
if (!this.getClass().isCloneable()) {
throw new CloneNotSupportedException();
}
// 调用 super.clone() 创建浅拷贝
MyClass cloned = (MyClass) super.clone();
// 如果需要深拷贝,可以在此处复制引用类型的成员
// cloned.setReference(this.reference.clone()); // 假设 reference 实现了 Cloneable
return cloned;
}
public static void main(String[] args) throws CloneNotSupportedException {
MyClass original = new MyClass(1, "Example", new Object());
MyClass cloned = (MyClass) original.clone();
System.out.println("Original ID: " + original.getId());
System.out.println("Cloned ID: " + cloned.getId());
System.out.println("Original Name: " + original.getName());
System.out.println("Cloned Name: " + cloned.getName());
System.out.println("Original Reference: " + original.getReference());
System.out.println("Cloned Reference: " + cloned.getReference());
}
}
运行结果:
Original ID: 1
Cloned ID: 1
Original Name: Example
Cloned Name: Example
Original Reference: java.lang.Object@hashcode
Cloned Reference: java.lang.Object@hashcode
6. toString()
源码:
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
返回由对象转化成的字符串。默认情况下,如上述代码,返回返回的是对象的类名加上 @
符号和十六进制格式的哈希码(表示这个类的内存地址)。该方法经常被重写。
7.notify()和 notifyAll()
源码:
public final native void notify();
public final native void notifyAll();
用来唤醒在该对象上等待的单个/所有线程。通常和下文提到的wait一起使用。
使用这些方法的注意事项:
(1)必须在同步代码块或者同步方法中调用它们
(2)调用它们之后,当前线程会释放被唤醒对象的锁,并且允许被唤醒的线程进入同步代码块执行
(3)如果没有任何线程正处于等待状态,调用它们不会产生任何效果
8.3个wait()方法
wait(long timeout)
源码:
public final native void wait(long timeout) throws InterruptedException;
让当前线程等待timeout(单位是毫秒)这么长时间。如果等待时间过了,或者线程被notify()或notifyAll()唤醒,线程才会继续执行。
wait()
源码:
public final void wait() throws InterruptedException {
wait(0);
}
当wait(0)的时候,这个线程等待的时间会被设置为无限长,也就是在notify之前一直等待。
wait(long timeout, int nanos)
源码:
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);
}
加入了nanos变量(纳秒),但通过观察代码不难发现,它只是判断表示纳秒的变量是否在正常范围内,如果在的话就会通过
if (nanos > 0) { timeout++; }
直接把表示毫秒的变量timeout+1,然后调用wait(long timeout)方法。
笔者不太理解创建这个方法的原因。它实际上并没有把时间精确到纳秒级,只是单纯地把等待的毫秒+1。有一种说法是,在多次调用wait的时候,用wait(long timeout)能让误差更小,但是没有解释具体原因。更早版本的JDK中,对应位置的代码如下:
if (nanos >= 500000 || (nanos != 0 && timeout == 0)) {timeout++;}
也就是,当纳秒不足0.5毫秒时不加1,纳秒大于0.5毫秒时再加1。所以还有一种说法就是,这个方法是当初想解决高并发问题挖的坑,后来发现其实不用这样,为了兼容之前的版本,就直接改成了所有情况都+1。
9. finalize()
源码:
protected void finalize() throws Throwable { }
这个是用来处理垃圾回收的方法。默认的时候为空,就是什么都不做。可以通过重写这个方法来实现自定义的垃圾回收功能。如果短时间内多次调用finalize()会影响程序的性能,因为java的垃圾回收器需要额外处理这些方法。
替代方案:使用try-with-resources语句。就是在try-catch语句中直接调用资源。在try-catch语句执行完以后,被调用的资源会被自动释放掉,比如:
try (BufferedReader reader = new BufferedReader(new FileReader(filePath))) {
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
} catch (IOException e) {
e.printStackTrace();
}
当上述代码被执行完以后,会自动关闭BufferReader。
当然,还可以自己手动关闭,比如调用close(),也有同样的效果。
以上内容仅为笔者在学习过程中,自己对于java中object类的一些理解和思考。欢迎讨论和指正。